IOFactory.php 5.9 KB

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