Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 402 and 403]

   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  namespace OpenSpout\Writer;
   6  
   7  use OpenSpout\Common\Entity\Row;
   8  use OpenSpout\Common\Exception\IOException;
   9  use OpenSpout\Writer\Exception\WriterNotOpenedException;
  10  
  11  abstract class AbstractWriter implements WriterInterface
  12  {
  13      /** @var resource Pointer to the file/stream we will write to */
  14      protected $filePointer;
  15  
  16      /** @var string document creator */
  17      protected string $creator = 'OpenSpout';
  18  
  19      /** @var string Content-Type value for the header - to be defined by child class */
  20      protected static string $headerContentType;
  21  
  22      /** @var string Path to the output file */
  23      private string $outputFilePath;
  24  
  25      /** @var bool Indicates whether the writer has been opened or not */
  26      private bool $isWriterOpened = false;
  27  
  28      /** @var 0|positive-int */
  29      private int $writtenRowCount = 0;
  30  
  31      final public function openToFile($outputFilePath): void
  32      {
  33          $this->outputFilePath = $outputFilePath;
  34  
  35          $errorMessage = null;
  36          set_error_handler(static function ($nr, $message) use (&$errorMessage): bool {
  37              $errorMessage = $message;
  38  
  39              return true;
  40          });
  41  
  42          $resource = fopen($this->outputFilePath, 'w');
  43          restore_error_handler();
  44          if (null !== $errorMessage) {
  45              throw new IOException("Unable to open file {$this->outputFilePath}: {$errorMessage}");
  46          }
  47          \assert(false !== $resource);
  48          $this->filePointer = $resource;
  49  
  50          $this->openWriter();
  51          $this->isWriterOpened = true;
  52      }
  53  
  54      /**
  55       * @codeCoverageIgnore
  56       *
  57       * @param mixed $outputFileName
  58       */
  59      final public function openToBrowser($outputFileName): void
  60      {
  61          $this->outputFilePath = basename($outputFileName);
  62  
  63          $resource = fopen('php://output', 'w');
  64          \assert(false !== $resource);
  65          $this->filePointer = $resource;
  66  
  67          // Clear any previous output (otherwise the generated file will be corrupted)
  68          // @see https://github.com/box/spout/issues/241
  69          if (ob_get_length() > 0) {
  70              ob_end_clean();
  71          }
  72  
  73          /*
  74           * Set headers
  75           *
  76           * For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*`
  77           * specified by the new standard, even if they do not automatically decode filename; it does not matter;
  78           * and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically
  79           * ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name).
  80           * In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not
  81           * require UA judgment and is more in line with the standard.
  82           *
  83           * @see https://github.com/box/spout/issues/745
  84           * @see https://tools.ietf.org/html/rfc6266
  85           * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
  86           */
  87          header('Content-Type: '.static::$headerContentType);
  88          header(
  89              'Content-Disposition: attachment; '.
  90              'filename="'.rawurlencode($this->outputFilePath).'"; '.
  91              'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath)
  92          );
  93  
  94          /*
  95           * When forcing the download of a file over SSL,IE8 and lower browsers fail
  96           * if the Cache-Control and Pragma headers are not set.
  97           *
  98           * @see http://support.microsoft.com/KB/323308
  99           * @see https://github.com/liuggio/ExcelBundle/issues/45
 100           */
 101          header('Cache-Control: max-age=0');
 102          header('Pragma: public');
 103  
 104          $this->openWriter();
 105          $this->isWriterOpened = true;
 106      }
 107  
 108      final public function addRow(Row $row): void
 109      {
 110          if (!$this->isWriterOpened) {
 111              throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
 112          }
 113  
 114          $this->addRowToWriter($row);
 115          ++$this->writtenRowCount;
 116      }
 117  
 118      final public function addRows(array $rows): void
 119      {
 120          foreach ($rows as $row) {
 121              $this->addRow($row);
 122          }
 123      }
 124  
 125      final public function setCreator(string $creator): void
 126      {
 127          $this->creator = $creator;
 128      }
 129  
 130      final public function getWrittenRowCount(): int
 131      {
 132          return $this->writtenRowCount;
 133      }
 134  
 135      final public function close(): void
 136      {
 137          if (!$this->isWriterOpened) {
 138              return;
 139          }
 140  
 141          $this->closeWriter();
 142  
 143          fclose($this->filePointer);
 144  
 145          $this->isWriterOpened = false;
 146      }
 147  
 148      /**
 149       * Opens the streamer and makes it ready to accept data.
 150       *
 151       * @throws IOException If the writer cannot be opened
 152       */
 153      abstract protected function openWriter(): void;
 154  
 155      /**
 156       * Adds a row to the currently opened writer.
 157       *
 158       * @param Row $row The row containing cells and styles
 159       *
 160       * @throws WriterNotOpenedException If the workbook is not created yet
 161       * @throws IOException              If unable to write data
 162       */
 163      abstract protected function addRowToWriter(Row $row): void;
 164  
 165      /**
 166       * Closes the streamer, preventing any additional writing.
 167       */
 168      abstract protected function closeWriter(): void;
 169  }