Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet;
   4  
   5  use PhpOffice\PhpSpreadsheet\Reader\IReader;
   6  use PhpOffice\PhpSpreadsheet\Shared\File;
   7  use PhpOffice\PhpSpreadsheet\Writer\IWriter;
   8  
   9  /**
  10   * Factory to create readers and writers easily.
  11   *
  12   * It is not required to use this class, but it should make it easier to read and write files.
  13   * Especially for reading files with an unknown format.
  14   */
  15  abstract class IOFactory
  16  {
  17      private static $readers = [
  18          'Xlsx' => Reader\Xlsx::class,
  19          'Xls' => Reader\Xls::class,
  20          'Xml' => Reader\Xml::class,
  21          'Ods' => Reader\Ods::class,
  22          'Slk' => Reader\Slk::class,
  23          'Gnumeric' => Reader\Gnumeric::class,
  24          'Html' => Reader\Html::class,
  25          'Csv' => Reader\Csv::class,
  26      ];
  27  
  28      private static $writers = [
  29          'Xls' => Writer\Xls::class,
  30          'Xlsx' => Writer\Xlsx::class,
  31          'Ods' => Writer\Ods::class,
  32          'Csv' => Writer\Csv::class,
  33          'Html' => Writer\Html::class,
  34          'Tcpdf' => Writer\Pdf\Tcpdf::class,
  35          'Dompdf' => Writer\Pdf\Dompdf::class,
  36          'Mpdf' => Writer\Pdf\Mpdf::class,
  37      ];
  38  
  39      /**
  40       * Create Writer\IWriter.
  41       */
  42      public static function createWriter(Spreadsheet $spreadsheet, string $writerType): IWriter
  43      {
  44          if (!isset(self::$writers[$writerType])) {
  45              throw new Writer\Exception("No writer found for type $writerType");
  46          }
  47  
  48          // Instantiate writer
  49          $className = self::$writers[$writerType];
  50  
  51          return new $className($spreadsheet);
  52      }
  53  
  54      /**
  55       * Create IReader.
  56       */
  57      public static function createReader(string $readerType): IReader
  58      {
  59          if (!isset(self::$readers[$readerType])) {
  60              throw new Reader\Exception("No reader found for type $readerType");
  61          }
  62  
  63          // Instantiate reader
  64          $className = self::$readers[$readerType];
  65  
  66          return new $className();
  67      }
  68  
  69      /**
  70       * Loads Spreadsheet from file using automatic Reader\IReader resolution.
  71       *
  72       * @param string $filename The name of the spreadsheet file
  73       */
  74      public static function load(string $filename, int $flags = 0): Spreadsheet
  75      {
  76          $reader = self::createReaderForFile($filename);
  77  
  78          return $reader->load($filename, $flags);
  79      }
  80  
  81      /**
  82       * Identify file type using automatic IReader resolution.
  83       */
  84      public static function identify(string $filename): string
  85      {
  86          $reader = self::createReaderForFile($filename);
  87          $className = get_class($reader);
  88          $classType = explode('\\', $className);
  89          unset($reader);
  90  
  91          return array_pop($classType);
  92      }
  93  
  94      /**
  95       * Create Reader\IReader for file using automatic IReader resolution.
  96       */
  97      public static function createReaderForFile(string $filename): IReader
  98      {
  99          File::assertFile($filename);
 100  
 101          // First, lucky guess by inspecting file extension
 102          $guessedReader = self::getReaderTypeFromExtension($filename);
 103          if ($guessedReader !== null) {
 104              $reader = self::createReader($guessedReader);
 105  
 106              // Let's see if we are lucky
 107              if ($reader->canRead($filename)) {
 108                  return $reader;
 109              }
 110          }
 111  
 112          // If we reach here then "lucky guess" didn't give any result
 113          // Try walking through all the options in self::$autoResolveClasses
 114          foreach (self::$readers as $type => $class) {
 115              //    Ignore our original guess, we know that won't work
 116              if ($type !== $guessedReader) {
 117                  $reader = self::createReader($type);
 118                  if ($reader->canRead($filename)) {
 119                      return $reader;
 120                  }
 121              }
 122          }
 123  
 124          throw new Reader\Exception('Unable to identify a reader for this file');
 125      }
 126  
 127      /**
 128       * Guess a reader type from the file extension, if any.
 129       */
 130      private static function getReaderTypeFromExtension(string $filename): ?string
 131      {
 132          $pathinfo = pathinfo($filename);
 133          if (!isset($pathinfo['extension'])) {
 134              return null;
 135          }
 136  
 137          switch (strtolower($pathinfo['extension'])) {
 138              case 'xlsx': // Excel (OfficeOpenXML) Spreadsheet
 139              case 'xlsm': // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
 140              case 'xltx': // Excel (OfficeOpenXML) Template
 141              case 'xltm': // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
 142                  return 'Xlsx';
 143              case 'xls': // Excel (BIFF) Spreadsheet
 144              case 'xlt': // Excel (BIFF) Template
 145                  return 'Xls';
 146              case 'ods': // Open/Libre Offic Calc
 147              case 'ots': // Open/Libre Offic Calc Template
 148                  return 'Ods';
 149              case 'slk':
 150                  return 'Slk';
 151              case 'xml': // Excel 2003 SpreadSheetML
 152                  return 'Xml';
 153              case 'gnumeric':
 154                  return 'Gnumeric';
 155              case 'htm':
 156              case 'html':
 157                  return 'Html';
 158              case 'csv':
 159                  // Do nothing
 160                  // We must not try to use CSV reader since it loads
 161                  // all files including Excel files etc.
 162                  return null;
 163              default:
 164                  return null;
 165          }
 166      }
 167  
 168      /**
 169       * Register a writer with its type and class name.
 170       */
 171      public static function registerWriter(string $writerType, string $writerClass): void
 172      {
 173          if (!is_a($writerClass, IWriter::class, true)) {
 174              throw new Writer\Exception('Registered writers must implement ' . IWriter::class);
 175          }
 176  
 177          self::$writers[$writerType] = $writerClass;
 178      }
 179  
 180      /**
 181       * Register a reader with its type and class name.
 182       */
 183      public static function registerReader(string $readerType, string $readerClass): void
 184      {
 185          if (!is_a($readerClass, IReader::class, true)) {
 186              throw new Reader\Exception('Registered readers must implement ' . IReader::class);
 187          }
 188  
 189          self::$readers[$readerType] = $readerClass;
 190      }
 191  }