IOFactory.php 6.7 KB

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