Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.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 Content-Type value for the header - to be defined by child class */
  17      protected static string $headerContentType;
  18  
  19      /** @var string Path to the output file */
  20      private string $outputFilePath;
  21  
  22      /** @var bool Indicates whether the writer has been opened or not */
  23      private bool $isWriterOpened = false;
  24  
  25      /** @var 0|positive-int */
  26      private int $writtenRowCount = 0;
  27  
  28      /**
  29       * {@inheritdoc}
  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       * {@inheritdoc}
  57       */
  58      final public function openToBrowser($outputFileName): void
  59      {
  60          $this->outputFilePath = basename($outputFileName);
  61  
  62          $resource = fopen('php://output', 'w');
  63          \assert(false !== $resource);
  64          $this->filePointer = $resource;
  65  
  66          // Clear any previous output (otherwise the generated file will be corrupted)
  67          // @see https://github.com/box/spout/issues/241
  68          if (ob_get_length() > 0) {
  69              ob_end_clean();
  70          }
  71  
  72          /*
  73           * Set headers
  74           *
  75           * For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*`
  76           * specified by the new standard, even if they do not automatically decode filename; it does not matter;
  77           * and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically
  78           * ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name).
  79           * In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not
  80           * require UA judgment and is more in line with the standard.
  81           *
  82           * @see https://github.com/box/spout/issues/745
  83           * @see https://tools.ietf.org/html/rfc6266
  84           * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
  85           */
  86          header('Content-Type: '.static::$headerContentType);
  87          header(
  88              'Content-Disposition: attachment; '.
  89              'filename="'.rawurlencode($this->outputFilePath).'"; '.
  90              'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath)
  91          );
  92  
  93          /*
  94           * When forcing the download of a file over SSL,IE8 and lower browsers fail
  95           * if the Cache-Control and Pragma headers are not set.
  96           *
  97           * @see http://support.microsoft.com/KB/323308
  98           * @see https://github.com/liuggio/ExcelBundle/issues/45
  99           */
 100          header('Cache-Control: max-age=0');
 101          header('Pragma: public');
 102  
 103          $this->openWriter();
 104          $this->isWriterOpened = true;
 105      }
 106  
 107      /**
 108       * {@inheritdoc}
 109       */
 110      final public function addRow(Row $row): void
 111      {
 112          if (!$this->isWriterOpened) {
 113              throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
 114          }
 115  
 116          $this->addRowToWriter($row);
 117          ++$this->writtenRowCount;
 118      }
 119  
 120      /**
 121       * {@inheritdoc}
 122       */
 123      final public function addRows(array $rows): void
 124      {
 125          foreach ($rows as $row) {
 126              $this->addRow($row);
 127          }
 128      }
 129  
 130      /**
 131       * {@inheritdoc}
 132       */
 133      final public function getWrittenRowCount(): int
 134      {
 135          return $this->writtenRowCount;
 136      }
 137  
 138      /**
 139       * {@inheritdoc}
 140       */
 141      final public function close(): void
 142      {
 143          if (!$this->isWriterOpened) {
 144              return;
 145          }
 146  
 147          $this->closeWriter();
 148  
 149          fclose($this->filePointer);
 150  
 151          $this->isWriterOpened = false;
 152      }
 153  
 154      /**
 155       * Opens the streamer and makes it ready to accept data.
 156       *
 157       * @throws IOException If the writer cannot be opened
 158       */
 159      abstract protected function openWriter(): void;
 160  
 161      /**
 162       * Adds a row to the currently opened writer.
 163       *
 164       * @param Row $row The row containing cells and styles
 165       *
 166       * @throws WriterNotOpenedException If the workbook is not created yet
 167       * @throws IOException              If unable to write data
 168       */
 169      abstract protected function addRowToWriter(Row $row): void;
 170  
 171      /**
 172       * Closes the streamer, preventing any additional writing.
 173       */
 174      abstract protected function closeWriter(): void;
 175  }