diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d60ca306b..5957fac338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Changed - Start migration to Phpstan 2. [PR #4359](https://github.com/PHPOffice/PhpSpreadsheet/pull/4359) +- IOFactory identify can return, and createReader and CreateWriter can accept, a class name rather than a file type. [Issue #4357](https://github.com/PHPOffice/PhpSpreadsheet/issues/4357) [PR #4361](https://github.com/PHPOffice/PhpSpreadsheet/pull/4361) ### Moved diff --git a/docs/topics/reading-files.md b/docs/topics/reading-files.md index 4aba019ff7..9b80f5b0d1 100644 --- a/docs/topics/reading-files.md +++ b/docs/topics/reading-files.md @@ -123,7 +123,10 @@ method to identify the reader that you need, before using the ```php $inputFileName = './sampleData/example1.xls'; -/** Identify the type of $inputFileName **/ +/** + * Identify the type of $inputFileName. + * See below for a possible improvement for release 4.1.0+. + */ $inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName); /** Create a new Reader of the type that has been identified **/ $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); @@ -134,6 +137,13 @@ $spreadsheet = $reader->load($inputFileName); See `samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php` for a working example of this code. +Prior to release 4.1.0, `identify` returns a file type. +It may be more useful to return a fully-qualified class name, +which can be accomplished using a parameter introduced in 4.1.0: +```php +$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName, null, true); +``` + As with the IOFactory `load()` method, you can also pass an array of formats for the `identify()` method to check against if you know that it will only be in a subset of the possible formats that PhpSpreadsheet supports. diff --git a/src/PhpSpreadsheet/IOFactory.php b/src/PhpSpreadsheet/IOFactory.php index db8776db61..73734fc702 100644 --- a/src/PhpSpreadsheet/IOFactory.php +++ b/src/PhpSpreadsheet/IOFactory.php @@ -59,12 +59,16 @@ abstract class IOFactory */ public static function createWriter(Spreadsheet $spreadsheet, string $writerType): IWriter { - if (!isset(self::$writers[$writerType])) { - throw new Writer\Exception("No writer found for type $writerType"); - } + /** @var class-string */ + $className = $writerType; + if (!in_array($writerType, self::$writers, true)) { + if (!isset(self::$writers[$writerType])) { + throw new Writer\Exception("No writer found for type $writerType"); + } - // Instantiate writer - $className = self::$writers[$writerType]; + // Instantiate writer + $className = self::$writers[$writerType]; + } return new $className($spreadsheet); } @@ -74,12 +78,16 @@ public static function createWriter(Spreadsheet $spreadsheet, string $writerType */ public static function createReader(string $readerType): IReader { - if (!isset(self::$readers[$readerType])) { - throw new Reader\Exception("No reader found for type $readerType"); - } + /** @var class-string */ + $className = $readerType; + if (!in_array($readerType, self::$readers, true)) { + if (!isset(self::$readers[$readerType])) { + throw new Reader\Exception("No reader found for type $readerType"); + } - // Instantiate reader - $className = self::$readers[$readerType]; + // Instantiate reader + $className = self::$readers[$readerType]; + } return new $className(); } @@ -109,12 +117,14 @@ public static function load(string $filename, int $flags = 0, ?array $readers = /** * Identify file type using automatic IReader resolution. */ - public static function identify(string $filename, ?array $readers = null): string + public static function identify(string $filename, ?array $readers = null, bool $fullClassName = false): string { $reader = self::createReaderForFile($filename, $readers); $className = $reader::class; + if ($fullClassName) { + return $className; + } $classType = explode('\\', $className); - unset($reader); return array_pop($classType); } @@ -224,6 +234,8 @@ public static function registerWriter(string $writerType, string $writerClass): /** * Register a reader with its type and class name. + * + * @param class-string $readerClass */ public static function registerReader(string $readerType, string $readerClass): void { diff --git a/tests/PhpSpreadsheetTests/CustomReader.php b/tests/PhpSpreadsheetTests/CustomReader.php new file mode 100644 index 0000000000..9d48ae8ef9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/CustomReader.php @@ -0,0 +1,12 @@ +expectException(Writer\Exception::class); + $this->expectExceptionMessage('writers must implement'); + IOFactory::registerWriter('foo', 'bar'); // @phpstan-ignore-line + } + + public function testRegisterInvalidReader(): void + { + $this->expectException(ReaderException::class); + $this->expectExceptionMessage('readers must implement'); + IOFactory::registerReader('foo', 'bar'); // @phpstan-ignore-line + } + + public static function testRegisterCustomReader(): void + { + IOFactory::registerReader(IOFactory::READER_XLSX, CustomReader::class); + $inputFileName = 'tests/data/Reader/XLSX/1900_Calendar.xlsx'; + $loadSpreadsheet = IOFactory::load($inputFileName); + $sheet = $loadSpreadsheet->getActiveSheet(); + self::assertSame('2022-01-01', $sheet->getCell('A1')->getFormattedValue()); + $loadSpreadsheet->disconnectWorksheets(); + + $reader = new CustomReader(); + $newSpreadsheet = $reader->load($inputFileName); + $newSheet = $newSpreadsheet->getActiveSheet(); + self::assertSame('2022-01-01', $newSheet->getCell('A1')->getFormattedValue()); + $newSpreadsheet->disconnectWorksheets(); + + $inputFileType = IOFactory::identify($inputFileName, null, true); + $objReader = IOFactory::createReader($inputFileType); + self::assertInstanceOf(CustomReader::class, $objReader); + $objSpreadsheet = $objReader->load($inputFileName); + $objSheet = $objSpreadsheet->getActiveSheet(); + self::assertSame('2022-01-01', $objSheet->getCell('A1')->getFormattedValue()); + $objSpreadsheet->disconnectWorksheets(); + } + + public static function testRegisterCustomWriter(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setCellValue('A1', 1); + $writer = new CustomWriter($spreadsheet); + $html = $writer->generateHtmlAll(); + self::assertStringContainsString('1', $html); + IOFactory::registerWriter(IOFactory::WRITER_HTML, CustomWriter::class); + $objWriter = IOFactory::createWriter($spreadsheet, CustomWriter::class); + self::assertInstanceOf(CustomWriter::class, $objWriter); + $html2 = $objWriter->generateHtmlAll(); + self::assertStringContainsString('1', $html2); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/IOFactoryTest.php b/tests/PhpSpreadsheetTests/IOFactoryTest.php index 7fb1d79e9a..bb56a158d1 100644 --- a/tests/PhpSpreadsheetTests/IOFactoryTest.php +++ b/tests/PhpSpreadsheetTests/IOFactoryTest.php @@ -159,21 +159,6 @@ public function testIdentifyExistingDirectoryThrowExceptions(): void IOFactory::identify('.'); } - public function testRegisterInvalidWriter(): void - { - $this->expectException(Writer\Exception::class); - - // @phpstan-ignore-next-line - IOFactory::registerWriter('foo', 'bar'); - } - - public function testRegisterInvalidReader(): void - { - $this->expectException(ReaderException::class); - - IOFactory::registerReader('foo', 'bar'); - } - public function testCreateInvalidWriter(): void { $this->expectException(Writer\Exception::class);