Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Class used for creating ZIP archives.
  19   *
  20   * @package   core_files
  21   * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_files\local\archive_writer;
  26  
  27  use ZipStream\Option\Archive;
  28  use ZipStream\ZipStream;
  29  use ZipStream\Option\File as FileOptions;
  30  use core_files\archive_writer;
  31  use core_files\local\archive_writer\file_writer_interface as file_writer_interface;
  32  use core_files\local\archive_writer\stream_writer_interface as stream_writer_interface;
  33  
  34  /**
  35   * Class used for creating ZIP archives.
  36   *
  37   * @package   core_files
  38   * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
  39   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class zip_writer extends archive_writer implements file_writer_interface, stream_writer_interface {
  42  
  43      /**
  44       * @var resource File resource for the file handle for a file-based zip stream
  45       */
  46      private $zipfilehandle = null;
  47  
  48      /**
  49       * @var String The location of the zip file.
  50       */
  51      private $zipfilepath = null;
  52  
  53      /**
  54       * @var ZipStream The zip stream.
  55       */
  56      private $archive;
  57  
  58      /**
  59       * The zip_writer constructor.
  60       *
  61       * @param ZipStream $archive
  62       */
  63      protected function __construct(ZipStream $archive) {
  64          parent::__construct();
  65          $this->archive = $archive;
  66      }
  67  
  68      public static function stream_instance(string $filename): stream_writer_interface {
  69          $options = new Archive();
  70          $options->setSendHttpHeaders(true);
  71          $options->setContentDisposition('attachment');
  72          $options->setContentType('application/x-zip');
  73          $zipwriter = new ZipStream($filename, $options);
  74  
  75          return new static($zipwriter);
  76      }
  77  
  78      public static function file_instance(string $filename): file_writer_interface {
  79          $dir = make_request_directory();
  80          $filepath = "$dir/$filename";
  81          $fh = fopen($filepath, 'w');
  82  
  83          $exportoptions = new Archive();
  84          $exportoptions->setOutputStream($fh);
  85          $exportoptions->setSendHttpHeaders(false);
  86          $zipstream = new ZipStream($filename, $exportoptions);
  87  
  88          $zipwriter = new static($zipstream);
  89          // ZipStream only takes a file handle resource.
  90          // It does not close this resource itself, and it does not know the location of this resource on disk.
  91          // Store references to the filehandle, and the location of the filepath in the new class so that the `finish()`
  92          // function can close the fh, and move the temporary file into place.
  93          // The filehandle must be closed when finishing the archive. ZipStream does not close it automatically.
  94          $zipwriter->zipfilehandle = $fh;
  95          $zipwriter->zipfilepath = $filepath;
  96  
  97          return $zipwriter;
  98      }
  99  
 100      public function add_file_from_filepath(string $name, string $path): void {
 101          $this->archive->addFileFromPath($this->sanitise_filepath($name), $path);
 102      }
 103  
 104      public function add_file_from_string(string $name, string $data): void {
 105          $this->archive->addFile($this->sanitise_filepath($name), $data);
 106      }
 107  
 108      public function add_file_from_stream(string $name, $stream): void {
 109          $this->archive->addFileFromStream($this->sanitise_filepath($name), $stream);
 110          fclose($stream);
 111      }
 112  
 113      public function add_file_from_stored_file(string $name, \stored_file $file): void {
 114          $datetime = new \DateTime();
 115          $datetime->setTimestamp($file->get_timemodified());
 116          $fileoptions = new FileOptions();
 117          $fileoptions->setTime($datetime);
 118          $filehandle = $file->get_content_file_handle();
 119          $this->archive->addFileFromStream($this->sanitise_filepath($name), $filehandle, $fileoptions);
 120          fclose($filehandle);
 121      }
 122  
 123      public function finish(): void {
 124          $this->archive->finish();
 125  
 126          if ($this->zipfilehandle) {
 127              fclose($this->zipfilehandle);
 128          }
 129      }
 130  
 131      public function get_path_to_zip(): string {
 132          return $this->zipfilepath;
 133      }
 134  
 135      public function sanitise_filepath(string $filepath): string {
 136          $filepath = parent::sanitise_filepath($filepath);
 137  
 138          return \ZipStream\File::filterFilename($filepath);
 139      }
 140  }