IOFactory.php 6.4 KB

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