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.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   1  <?php
   2  
   3  namespace Box\Spout\Reader;
   4  
   5  use Box\Spout\Common\Exception\IOException;
   6  use Box\Spout\Common\Helper\GlobalFunctionsHelper;
   7  use Box\Spout\Common\Manager\OptionsManagerInterface;
   8  use Box\Spout\Reader\Common\Creator\InternalEntityFactoryInterface;
   9  use Box\Spout\Reader\Common\Entity\Options;
  10  use Box\Spout\Reader\Exception\ReaderNotOpenedException;
  11  
  12  /**
  13   * Class ReaderAbstract
  14   *
  15   * @abstract
  16   */
  17  abstract class ReaderAbstract implements ReaderInterface
  18  {
  19      /** @var bool Indicates whether the stream is currently open */
  20      protected $isStreamOpened = false;
  21  
  22      /** @var InternalEntityFactoryInterface Factory to create entities */
  23      protected $entityFactory;
  24  
  25      /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
  26      protected $globalFunctionsHelper;
  27  
  28      /** @var OptionsManagerInterface Writer options manager */
  29      protected $optionsManager;
  30  
  31      /**
  32       * Returns whether stream wrappers are supported
  33       *
  34       * @return bool
  35       */
  36      abstract protected function doesSupportStreamWrapper();
  37  
  38      /**
  39       * Opens the file at the given file path to make it ready to be read
  40       *
  41       * @param  string $filePath Path of the file to be read
  42       * @return void
  43       */
  44      abstract protected function openReader($filePath);
  45  
  46      /**
  47       * Returns an iterator to iterate over sheets.
  48       *
  49       * @return IteratorInterface To iterate over sheets
  50       */
  51      abstract protected function getConcreteSheetIterator();
  52  
  53      /**
  54       * Closes the reader. To be used after reading the file.
  55       *
  56       * @return ReaderAbstract
  57       */
  58      abstract protected function closeReader();
  59  
  60      /**
  61       * @param OptionsManagerInterface $optionsManager
  62       * @param GlobalFunctionsHelper $globalFunctionsHelper
  63       * @param InternalEntityFactoryInterface $entityFactory
  64       */
  65      public function __construct(
  66          OptionsManagerInterface $optionsManager,
  67          GlobalFunctionsHelper $globalFunctionsHelper,
  68          InternalEntityFactoryInterface $entityFactory
  69      ) {
  70          $this->optionsManager = $optionsManager;
  71          $this->globalFunctionsHelper = $globalFunctionsHelper;
  72          $this->entityFactory = $entityFactory;
  73      }
  74  
  75      /**
  76       * Sets whether date/time values should be returned as PHP objects or be formatted as strings.
  77       *
  78       * @param bool $shouldFormatDates
  79       * @return ReaderAbstract
  80       */
  81      public function setShouldFormatDates($shouldFormatDates)
  82      {
  83          $this->optionsManager->setOption(Options::SHOULD_FORMAT_DATES, $shouldFormatDates);
  84  
  85          return $this;
  86      }
  87  
  88      /**
  89       * Sets whether empty rows should be returned or skipped.
  90       *
  91       * @param bool $shouldPreserveEmptyRows
  92       * @return ReaderAbstract
  93       */
  94      public function setShouldPreserveEmptyRows($shouldPreserveEmptyRows)
  95      {
  96          $this->optionsManager->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, $shouldPreserveEmptyRows);
  97  
  98          return $this;
  99      }
 100  
 101      /**
 102       * Prepares the reader to read the given file. It also makes sure
 103       * that the file exists and is readable.
 104       *
 105       * @param  string $filePath Path of the file to be read
 106       * @throws \Box\Spout\Common\Exception\IOException If the file at the given path does not exist, is not readable or is corrupted
 107       * @return void
 108       */
 109      public function open($filePath)
 110      {
 111          if ($this->isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) {
 112              throw new IOException("Could not open $filePath for reading! Stream wrapper used is not supported for this type of file.");
 113          }
 114  
 115          if (!$this->isPhpStream($filePath)) {
 116              // we skip the checks if the provided file path points to a PHP stream
 117              if (!$this->globalFunctionsHelper->file_exists($filePath)) {
 118                  throw new IOException("Could not open $filePath for reading! File does not exist.");
 119              }
 120              if (!$this->globalFunctionsHelper->is_readable($filePath)) {
 121                  throw new IOException("Could not open $filePath for reading! File is not readable.");
 122              }
 123          }
 124  
 125          try {
 126              $fileRealPath = $this->getFileRealPath($filePath);
 127              $this->openReader($fileRealPath);
 128              $this->isStreamOpened = true;
 129          } catch (\Exception $exception) {
 130              throw new IOException("Could not open $filePath for reading! ({$exception->getMessage()})");
 131          }
 132      }
 133  
 134      /**
 135       * Returns the real path of the given path.
 136       * If the given path is a valid stream wrapper, returns the path unchanged.
 137       *
 138       * @param string $filePath
 139       * @return string
 140       */
 141      protected function getFileRealPath($filePath)
 142      {
 143          if ($this->isSupportedStreamWrapper($filePath)) {
 144              return $filePath;
 145          }
 146  
 147          // Need to use realpath to fix "Can't open file" on some Windows setup
 148          return \realpath($filePath);
 149      }
 150  
 151      /**
 152       * Returns the scheme of the custom stream wrapper, if the path indicates a stream wrapper is used.
 153       * For example, php://temp => php, s3://path/to/file => s3...
 154       *
 155       * @param string $filePath Path of the file to be read
 156       * @return string|null The stream wrapper scheme or NULL if not a stream wrapper
 157       */
 158      protected function getStreamWrapperScheme($filePath)
 159      {
 160          $streamScheme = null;
 161          if (\preg_match('/^(\w+):\/\//', $filePath, $matches)) {
 162              $streamScheme = $matches[1];
 163          }
 164  
 165          return $streamScheme;
 166      }
 167  
 168      /**
 169       * Checks if the given path is an unsupported stream wrapper
 170       * (like local path, php://temp, mystream://foo/bar...).
 171       *
 172       * @param string $filePath Path of the file to be read
 173       * @return bool Whether the given path is an unsupported stream wrapper
 174       */
 175      protected function isStreamWrapper($filePath)
 176      {
 177          return ($this->getStreamWrapperScheme($filePath) !== null);
 178      }
 179  
 180      /**
 181       * Checks if the given path is an supported stream wrapper
 182       * (like php://temp, mystream://foo/bar...).
 183       * If the given path is a local path, returns true.
 184       *
 185       * @param string $filePath Path of the file to be read
 186       * @return bool Whether the given path is an supported stream wrapper
 187       */
 188      protected function isSupportedStreamWrapper($filePath)
 189      {
 190          $streamScheme = $this->getStreamWrapperScheme($filePath);
 191  
 192          return ($streamScheme !== null) ?
 193              \in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers()) :
 194              true;
 195      }
 196  
 197      /**
 198       * Checks if a path is a PHP stream (like php://output, php://memory, ...)
 199       *
 200       * @param string $filePath Path of the file to be read
 201       * @return bool Whether the given path maps to a PHP stream
 202       */
 203      protected function isPhpStream($filePath)
 204      {
 205          $streamScheme = $this->getStreamWrapperScheme($filePath);
 206  
 207          return ($streamScheme === 'php');
 208      }
 209  
 210      /**
 211       * Returns an iterator to iterate over sheets.
 212       *
 213       * @throws \Box\Spout\Reader\Exception\ReaderNotOpenedException If called before opening the reader
 214       * @return \Iterator To iterate over sheets
 215       */
 216      public function getSheetIterator()
 217      {
 218          if (!$this->isStreamOpened) {
 219              throw new ReaderNotOpenedException('Reader should be opened first.');
 220          }
 221  
 222          return $this->getConcreteSheetIterator();
 223      }
 224  
 225      /**
 226       * Closes the reader, preventing any additional reading
 227       *
 228       * @return void
 229       */
 230      public function close()
 231      {
 232          if ($this->isStreamOpened) {
 233              $this->closeReader();
 234  
 235              $sheetIterator = $this->getConcreteSheetIterator();
 236              if ($sheetIterator) {
 237                  $sheetIterator->end();
 238              }
 239  
 240              $this->isStreamOpened = false;
 241          }
 242      }
 243  }