Skip to content

Commit 2e998d6

Browse files
authored
Merge pull request #4361 from oleibman/issue4357
IOFactory::identify and Custom Reader/Writer
2 parents c531f5d + 3675f08 commit 2e998d6

File tree

7 files changed

+147
-28
lines changed

7 files changed

+147
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
1818
### Changed
1919

2020
- Start migration to Phpstan 2. [PR #4359](https://github.com/PHPOffice/PhpSpreadsheet/pull/4359)
21+
- 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)
2122

2223
### Moved
2324

docs/topics/reading-files.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ method to identify the reader that you need, before using the
123123
```php
124124
$inputFileName = './sampleData/example1.xls';
125125

126-
/** Identify the type of $inputFileName **/
126+
/**
127+
* Identify the type of $inputFileName.
128+
* See below for a possible improvement for release 4.1.0+.
129+
*/
127130
$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName);
128131
/** Create a new Reader of the type that has been identified **/
129132
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
@@ -134,6 +137,13 @@ $spreadsheet = $reader->load($inputFileName);
134137
See `samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php`
135138
for a working example of this code.
136139

140+
Prior to release 4.1.0, `identify` returns a file type.
141+
It may be more useful to return a fully-qualified class name,
142+
which can be accomplished using a parameter introduced in 4.1.0:
143+
```php
144+
$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName, null, true);
145+
```
146+
137147
As with the IOFactory `load()` method, you can also pass an array of formats
138148
for the `identify()` method to check against if you know that it will only
139149
be in a subset of the possible formats that PhpSpreadsheet supports.

src/PhpSpreadsheet/IOFactory.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,16 @@ abstract class IOFactory
5959
*/
6060
public static function createWriter(Spreadsheet $spreadsheet, string $writerType): IWriter
6161
{
62-
if (!isset(self::$writers[$writerType])) {
63-
throw new Writer\Exception("No writer found for type $writerType");
64-
}
62+
/** @var class-string<IWriter> */
63+
$className = $writerType;
64+
if (!in_array($writerType, self::$writers, true)) {
65+
if (!isset(self::$writers[$writerType])) {
66+
throw new Writer\Exception("No writer found for type $writerType");
67+
}
6568

66-
// Instantiate writer
67-
$className = self::$writers[$writerType];
69+
// Instantiate writer
70+
$className = self::$writers[$writerType];
71+
}
6872

6973
return new $className($spreadsheet);
7074
}
@@ -74,12 +78,16 @@ public static function createWriter(Spreadsheet $spreadsheet, string $writerType
7478
*/
7579
public static function createReader(string $readerType): IReader
7680
{
77-
if (!isset(self::$readers[$readerType])) {
78-
throw new Reader\Exception("No reader found for type $readerType");
79-
}
81+
/** @var class-string<IReader> */
82+
$className = $readerType;
83+
if (!in_array($readerType, self::$readers, true)) {
84+
if (!isset(self::$readers[$readerType])) {
85+
throw new Reader\Exception("No reader found for type $readerType");
86+
}
8087

81-
// Instantiate reader
82-
$className = self::$readers[$readerType];
88+
// Instantiate reader
89+
$className = self::$readers[$readerType];
90+
}
8391

8492
return new $className();
8593
}
@@ -109,12 +117,14 @@ public static function load(string $filename, int $flags = 0, ?array $readers =
109117
/**
110118
* Identify file type using automatic IReader resolution.
111119
*/
112-
public static function identify(string $filename, ?array $readers = null): string
120+
public static function identify(string $filename, ?array $readers = null, bool $fullClassName = false): string
113121
{
114122
$reader = self::createReaderForFile($filename, $readers);
115123
$className = $reader::class;
124+
if ($fullClassName) {
125+
return $className;
126+
}
116127
$classType = explode('\\', $className);
117-
unset($reader);
118128

119129
return array_pop($classType);
120130
}
@@ -224,6 +234,8 @@ public static function registerWriter(string $writerType, string $writerClass):
224234

225235
/**
226236
* Register a reader with its type and class name.
237+
*
238+
* @param class-string<IReader> $readerClass
227239
*/
228240
public static function registerReader(string $readerType, string $readerClass): void
229241
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
9+
// Used in IOFactoryRegister tests
10+
class CustomReader extends XlsxReader
11+
{
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests;
6+
7+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
8+
9+
// Used in IOFactoryRegister tests
10+
class CustomWriter extends HtmlWriter
11+
{
12+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests;
6+
7+
use PhpOffice\PhpSpreadsheet\IOFactory;
8+
use PhpOffice\PhpSpreadsheet\Reader;
9+
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
10+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
11+
use PhpOffice\PhpSpreadsheet\Writer;
12+
use PHPUnit\Framework\Attributes;
13+
use PHPUnit\Framework\TestCase;
14+
15+
// Separate processes because register arrays are static
16+
#[Attributes\RunTestsInSeparateProcesses]
17+
class IOFactoryRegisterTest extends TestCase
18+
{
19+
public function testRegisterWriter(): void
20+
{
21+
IOFactory::registerWriter('Pdf', Writer\Pdf\Mpdf::class);
22+
$spreadsheet = new Spreadsheet();
23+
$actual = IOFactory::createWriter($spreadsheet, 'Pdf');
24+
self::assertInstanceOf(Writer\Pdf\Mpdf::class, $actual);
25+
}
26+
27+
public function testRegisterReader(): void
28+
{
29+
IOFactory::registerReader('Custom', Reader\Html::class);
30+
$actual = IOFactory::createReader('Custom');
31+
self::assertInstanceOf(Reader\Html::class, $actual);
32+
}
33+
34+
public function testRegisterInvalidWriter(): void
35+
{
36+
$this->expectException(Writer\Exception::class);
37+
$this->expectExceptionMessage('writers must implement');
38+
IOFactory::registerWriter('foo', 'bar'); // @phpstan-ignore-line
39+
}
40+
41+
public function testRegisterInvalidReader(): void
42+
{
43+
$this->expectException(ReaderException::class);
44+
$this->expectExceptionMessage('readers must implement');
45+
IOFactory::registerReader('foo', 'bar'); // @phpstan-ignore-line
46+
}
47+
48+
public static function testRegisterCustomReader(): void
49+
{
50+
IOFactory::registerReader(IOFactory::READER_XLSX, CustomReader::class);
51+
$inputFileName = 'tests/data/Reader/XLSX/1900_Calendar.xlsx';
52+
$loadSpreadsheet = IOFactory::load($inputFileName);
53+
$sheet = $loadSpreadsheet->getActiveSheet();
54+
self::assertSame('2022-01-01', $sheet->getCell('A1')->getFormattedValue());
55+
$loadSpreadsheet->disconnectWorksheets();
56+
57+
$reader = new CustomReader();
58+
$newSpreadsheet = $reader->load($inputFileName);
59+
$newSheet = $newSpreadsheet->getActiveSheet();
60+
self::assertSame('2022-01-01', $newSheet->getCell('A1')->getFormattedValue());
61+
$newSpreadsheet->disconnectWorksheets();
62+
63+
$inputFileType = IOFactory::identify($inputFileName, null, true);
64+
$objReader = IOFactory::createReader($inputFileType);
65+
self::assertInstanceOf(CustomReader::class, $objReader);
66+
$objSpreadsheet = $objReader->load($inputFileName);
67+
$objSheet = $objSpreadsheet->getActiveSheet();
68+
self::assertSame('2022-01-01', $objSheet->getCell('A1')->getFormattedValue());
69+
$objSpreadsheet->disconnectWorksheets();
70+
}
71+
72+
public static function testRegisterCustomWriter(): void
73+
{
74+
$spreadsheet = new Spreadsheet();
75+
$sheet = $spreadsheet->getActiveSheet();
76+
$sheet->setCellValue('A1', 1);
77+
$writer = new CustomWriter($spreadsheet);
78+
$html = $writer->generateHtmlAll();
79+
self::assertStringContainsString('<td class="column0 style0 n">1</td>', $html);
80+
IOFactory::registerWriter(IOFactory::WRITER_HTML, CustomWriter::class);
81+
$objWriter = IOFactory::createWriter($spreadsheet, CustomWriter::class);
82+
self::assertInstanceOf(CustomWriter::class, $objWriter);
83+
$html2 = $objWriter->generateHtmlAll();
84+
self::assertStringContainsString('<td class="column0 style0 n">1</td>', $html2);
85+
$spreadsheet->disconnectWorksheets();
86+
}
87+
}

tests/PhpSpreadsheetTests/IOFactoryTest.php

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -159,21 +159,6 @@ public function testIdentifyExistingDirectoryThrowExceptions(): void
159159
IOFactory::identify('.');
160160
}
161161

162-
public function testRegisterInvalidWriter(): void
163-
{
164-
$this->expectException(Writer\Exception::class);
165-
166-
// @phpstan-ignore-next-line
167-
IOFactory::registerWriter('foo', 'bar');
168-
}
169-
170-
public function testRegisterInvalidReader(): void
171-
{
172-
$this->expectException(ReaderException::class);
173-
174-
IOFactory::registerReader('foo', 'bar');
175-
}
176-
177162
public function testCreateInvalidWriter(): void
178163
{
179164
$this->expectException(Writer\Exception::class);

0 commit comments

Comments
 (0)