From 19724e32175cd1b7a5c9d4205eb6b4129d35a6ad Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 4 Jun 2021 13:45:32 +0200 Subject: [PATCH] Reader writer flags (#2136) * Use of passing flags with Readers to identify whether speacial features such as loading charts should be enabled; no need to instantiate a reader and manually enable it before loading any more. This is in preparation for supporting new "boolean" Reaer/Writer features, such as pivot tables * Use of passing flags with Writers to identify whether speacial features such as loading charts should be enabled; no need to instantiate a writer and manually enable it before loading any more. * Update documentation with details of changes to the StringValueBinder --- CHANGELOG.md | 2 + docs/topics/reading-and-writing-to-file.md | 70 +++++++++++++++++-- phpstan-baseline.neon | 5 -- src/PhpSpreadsheet/IOFactory.php | 8 +-- src/PhpSpreadsheet/Reader/BaseReader.php | 7 ++ src/PhpSpreadsheet/Reader/Csv.php | 6 +- src/PhpSpreadsheet/Reader/Gnumeric.php | 6 +- src/PhpSpreadsheet/Reader/Html.php | 6 +- src/PhpSpreadsheet/Reader/IReader.php | 6 +- src/PhpSpreadsheet/Reader/Ods.php | 6 +- src/PhpSpreadsheet/Reader/Slk.php | 6 +- src/PhpSpreadsheet/Reader/Xls.php | 6 +- src/PhpSpreadsheet/Reader/Xlsx.php | 5 +- src/PhpSpreadsheet/Reader/Xml.php | 8 +-- src/PhpSpreadsheet/Writer/BaseWriter.php | 9 ++- src/PhpSpreadsheet/Writer/Csv.php | 4 +- src/PhpSpreadsheet/Writer/Html.php | 4 +- src/PhpSpreadsheet/Writer/IWriter.php | 4 +- src/PhpSpreadsheet/Writer/Ods.php | 4 +- src/PhpSpreadsheet/Writer/Pdf/Dompdf.php | 2 +- src/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 2 +- src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 2 +- src/PhpSpreadsheet/Writer/Xls.php | 4 +- src/PhpSpreadsheet/Writer/Xlsx.php | 4 +- .../Reader/SheetsXlsxChartTest.php | 5 +- 25 files changed, 136 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba74ff7a..82289a0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added +- Support for passing flags in the Reader `load()` and Writer `save()`methods, and through the IOFactory, to set behaviours. [PR #2136](https://github.com/PHPOffice/PhpSpreadsheet/pull/2136) + - See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/reading-and-writing-to-file/) for details - More flexibility in the StringValueBinder to determine what datatypes should be treated as strings [PR #2138](https://github.com/PHPOffice/PhpSpreadsheet/pull/2138) ### Changed diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index e5c2afd9..cb1e0e77 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -1,8 +1,7 @@ # Reading and writing to file As you already know from the [architecture](./architecture.md#readers-and-writers), -reading and writing to a -persisted storage is not possible using the base PhpSpreadsheet classes. +reading and writing to a persisted storage is not possible using the base PhpSpreadsheet classes. For this purpose, PhpSpreadsheet provides readers and writers, which are implementations of `\PhpOffice\PhpSpreadsheet\Reader\IReader` and `\PhpOffice\PhpSpreadsheet\Writer\IWriter`. @@ -892,8 +891,7 @@ class My_Custom_TCPDF_Writer extends \PhpOffice\PhpSpreadsheet\Writer\Pdf\Tcpdf #### Writing a spreadsheet -Once you have identified the Renderer that you wish to use for PDF -generation, you can write a .pdf file using the following code: +Once you have identified the Renderer that you wish to use for PDF generation, you can write a .pdf file using the following code: ```php $writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet); @@ -905,8 +903,7 @@ first worksheet by default. #### Write all worksheets -PDF files can contain one or more worksheets. If you want to write all -sheets into a single PDF file, use the following code: +PDF files can contain one or more worksheets. If you want to write all sheets into a single PDF file, use the following code: ```php $writer->writeAllSheets(); @@ -1020,3 +1017,64 @@ $spreadhseet = $reader->loadFromString($secondHtmlString, $spreadsheet); $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls'); $writer->save('write.xls'); ``` + +## Reader/Writer Flags + +Some Readers and Writers support special "Feature Flags" that need to be explicitly enabled. +An example of this is Charts in a spreadsheet. By default, when you load a spreadsheet that contains Charts, the charts will not be loaded. If all you want to do is read the data in the spreadsheet, then loading charts is an overhead for both speed of loading and memory usage. +However, there are times when you may want to load any charts in the spreadsheet as well as the data. To do so, you need to tell the Reader explicitly to include Charts. + +```php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("05featuredemo.xlsx"); +$reader->setIncludeCharts(true); +$reader->load("spreadsheetWithCharts.xlsx"); +``` +Alternatively, you can specify this in the call to load the spreadsheet: +```php +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("spreadsheetWithCharts.xlsx"); +$reader->load("spreadsheetWithCharts.xlsx", $reader::LOAD_WITH_CHARTS); +``` + +If you wish to use the IOFactory `load()` method rather than instantiating a specific Reader, then you can still pass these flags. + +```php +$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load("spreadsheetWithCharts.xlsx", \PhpOffice\PhpSpreadsheet\Reader\IReader::LOAD_WITH_CHARTS); +``` + +Likewise, when saving a file using a Writer, loaded charts wil not be saved unless you explicitly tell the Writer to include them: + +```php +$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer->setIncludeCharts(true); +$writer->save('mySavedFileWithCharts.xlsx'); +``` + +As with the `load()` method, you can also pass flags in the `save()` method: +```php +$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer->save('mySavedFileWithCharts.xlsx', \PhpOffice\PhpSpreadsheet\Writer\IWriter::SAVE_WITH_CHARTS); +``` + +Currently, the only special "Feature Flag" that is supported in this way is the inclusion of Charts, and only for certain formats. + +Readers | LOAD_WITH_CHARTS | +---------|------------------| +Xlsx | YES | +Xls | NO | +Xml | NO | +Ods | NO | +Gnumeric | NO | +Html | N/A | +Slk | N/A | +Csv | N/A | + + +Writers | SAVE_WITH_CHARTS | +--------|------------------| +Xlsx | YES | +Xls | NO | +Ods | NO | +Html | YES | +Pdf | YES | +Csv | N/A | + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4ee0a9ef..4d4f7193 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6245,11 +6245,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Html.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\IWriter\\:\\:save\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/IWriter.php - - message: "#^Negated boolean expression is always false\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/IOFactory.php b/src/PhpSpreadsheet/IOFactory.php index 06006edc..6ed1fd82 100644 --- a/src/PhpSpreadsheet/IOFactory.php +++ b/src/PhpSpreadsheet/IOFactory.php @@ -75,15 +75,15 @@ abstract class IOFactory /** * Loads Spreadsheet from file using automatic Reader\IReader resolution. * - * @param string $pFilename The name of the spreadsheet file + * @param string $filename The name of the spreadsheet file * * @return Spreadsheet */ - public static function load($pFilename) + public static function load(string $filename, int $flags = 0) { - $reader = self::createReaderForFile($pFilename); + $reader = self::createReaderForFile($filename); - return $reader->load($pFilename); + return $reader->load($filename, $flags); } /** diff --git a/src/PhpSpreadsheet/Reader/BaseReader.php b/src/PhpSpreadsheet/Reader/BaseReader.php index 80348132..1f18484f 100644 --- a/src/PhpSpreadsheet/Reader/BaseReader.php +++ b/src/PhpSpreadsheet/Reader/BaseReader.php @@ -137,6 +137,13 @@ abstract class BaseReader implements IReader return $this->securityScanner; } + protected function processFlags(int $flags): void + { + if (((bool) ($flags & self::LOAD_WITH_CHARTS)) === true) { + $this->setIncludeCharts(true); + } + } + /** * Open file for reading. * diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index b7bc0d49..f06de635 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -236,12 +236,12 @@ class Csv extends BaseReader /** * Loads Spreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 85bae6f8..64c27366 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -235,12 +235,12 @@ class Gnumeric extends BaseReader /** * Loads Spreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); $spreadsheet->removeSheetByIndex(0); diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index b7faac87..6e6155c2 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -204,12 +204,12 @@ class Html extends BaseReader /** * Loads Spreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Reader/IReader.php b/src/PhpSpreadsheet/Reader/IReader.php index a8bd3606..6a57f4a0 100644 --- a/src/PhpSpreadsheet/Reader/IReader.php +++ b/src/PhpSpreadsheet/Reader/IReader.php @@ -4,6 +4,8 @@ namespace PhpOffice\PhpSpreadsheet\Reader; interface IReader { + public const LOAD_WITH_CHARTS = 1; + /** * IReader constructor. */ @@ -125,9 +127,7 @@ interface IReader /** * Loads PhpSpreadsheet from file. * - * @param string $pFilename - * * @return \PhpOffice\PhpSpreadsheet\Spreadsheet */ - public function load($pFilename); + public function load(string $pFilename, int $flags = 0); } diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 760aa10d..b39fffed 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -231,12 +231,12 @@ class Ods extends BaseReader /** * Loads PhpSpreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Reader/Slk.php b/src/PhpSpreadsheet/Reader/Slk.php index c7b6fc82..b58fdcba 100644 --- a/src/PhpSpreadsheet/Reader/Slk.php +++ b/src/PhpSpreadsheet/Reader/Slk.php @@ -197,12 +197,12 @@ class Slk extends BaseReader /** * Loads PhpSpreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index bfdd5583..a8c097f7 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -622,12 +622,12 @@ class Xls extends BaseReader /** * Loads PhpSpreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Read the OLE file $this->loadOLE($pFilename); diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 82e4e82d..8adb3bc8 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -310,13 +310,12 @@ class Xlsx extends BaseReader /** * Loads Spreadsheet from file. * - * @param string $pFilename - * * @return Spreadsheet */ - public function load($pFilename) + public function load(string $pFilename, int $flags = 0) { File::assertFile($pFilename); + $this->processFlags($flags); // Initialisations $excel = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index 4ef4efe7..f63bc798 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -236,18 +236,18 @@ class Xml extends BaseReader /** * Loads Spreadsheet from file. * - * @param string $filename - * * @return Spreadsheet */ - public function load($filename) + public function load(string $pFilename, int $flags = 0) { + $this->processFlags($flags); + // Create new Spreadsheet $spreadsheet = new Spreadsheet(); $spreadsheet->removeSheetByIndex(0); // Load into this instance - return $this->loadIntoExisting($filename, $spreadsheet); + return $this->loadIntoExisting($pFilename, $spreadsheet); } /** diff --git a/src/PhpSpreadsheet/Writer/BaseWriter.php b/src/PhpSpreadsheet/Writer/BaseWriter.php index afda5c43..3b03cfc3 100644 --- a/src/PhpSpreadsheet/Writer/BaseWriter.php +++ b/src/PhpSpreadsheet/Writer/BaseWriter.php @@ -6,7 +6,7 @@ abstract class BaseWriter implements IWriter { /** * Write charts that are defined in the workbook? - * Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object;. + * Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object. * * @var bool */ @@ -94,6 +94,13 @@ abstract class BaseWriter implements IWriter return $this->diskCachingDirectory; } + protected function processFlags(int $flags): void + { + if (((bool) ($flags & self::SAVE_WITH_CHARTS)) === true) { + $this->setIncludeCharts(true); + } + } + /** * Open file handle. * diff --git a/src/PhpSpreadsheet/Writer/Csv.php b/src/PhpSpreadsheet/Writer/Csv.php index 188a83a8..79e8e5f1 100644 --- a/src/PhpSpreadsheet/Writer/Csv.php +++ b/src/PhpSpreadsheet/Writer/Csv.php @@ -86,8 +86,10 @@ class Csv extends BaseWriter * * @param resource|string $pFilename */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { + $this->processFlags($flags); + // Fetch sheet $sheet = $this->spreadsheet->getSheet($this->sheetIndex); diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index b694d4ef..76c5e722 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -153,8 +153,10 @@ class Html extends BaseWriter * * @param resource|string $pFilename */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { + $this->processFlags($flags); + // Open file $this->openFileHandle($pFilename); diff --git a/src/PhpSpreadsheet/Writer/IWriter.php b/src/PhpSpreadsheet/Writer/IWriter.php index 5129d655..a0361f74 100644 --- a/src/PhpSpreadsheet/Writer/IWriter.php +++ b/src/PhpSpreadsheet/Writer/IWriter.php @@ -6,6 +6,8 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; interface IWriter { + public const SAVE_WITH_CHARTS = 1; + /** * IWriter constructor. */ @@ -59,7 +61,7 @@ interface IWriter * * @param resource|string $pFilename Name of the file to save */ - public function save($pFilename); + public function save($pFilename, int $flags = 0): void; /** * Get use disk caching where possible? diff --git a/src/PhpSpreadsheet/Writer/Ods.php b/src/PhpSpreadsheet/Writer/Ods.php index f07ade9a..141a4119 100644 --- a/src/PhpSpreadsheet/Writer/Ods.php +++ b/src/PhpSpreadsheet/Writer/Ods.php @@ -115,12 +115,14 @@ class Ods extends BaseWriter * * @param resource|string $pFilename */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { if (!$this->spreadSheet) { throw new WriterException('PhpSpreadsheet object unassigned.'); } + $this->processFlags($flags); + // garbage collect $this->spreadSheet->garbageCollect(); diff --git a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php index 87e8eeb5..313b34e8 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php @@ -22,7 +22,7 @@ class Dompdf extends Pdf * * @param string $pFilename Name of the file to save as */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php index 56ac6930..0db164b6 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php @@ -24,7 +24,7 @@ class Mpdf extends Pdf * * @param string $pFilename Name of the file to save as */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php index 56e917e3..76f983a0 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php @@ -38,7 +38,7 @@ class Tcpdf extends Pdf * * @param string $pFilename Name of the file to save as */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { $fileHandle = parent::prepareForSave($pFilename); diff --git a/src/PhpSpreadsheet/Writer/Xls.php b/src/PhpSpreadsheet/Writer/Xls.php index 624dc414..fa782755 100644 --- a/src/PhpSpreadsheet/Writer/Xls.php +++ b/src/PhpSpreadsheet/Writer/Xls.php @@ -119,8 +119,10 @@ class Xls extends BaseWriter * * @param resource|string $pFilename */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { + $this->processFlags($flags); + // garbage collect $this->spreadsheet->garbageCollect(); diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index 27ceb559..6f43989f 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -286,8 +286,10 @@ class Xlsx extends BaseWriter * * @param resource|string $pFilename */ - public function save($pFilename): void + public function save($pFilename, int $flags = 0): void { + $this->processFlags($flags); + // garbage collect $this->pathNames = []; $this->spreadSheet->garbageCollect(); diff --git a/tests/PhpSpreadsheetTests/Reader/SheetsXlsxChartTest.php b/tests/PhpSpreadsheetTests/Reader/SheetsXlsxChartTest.php index c4dc328d..9c478c4a 100644 --- a/tests/PhpSpreadsheetTests/Reader/SheetsXlsxChartTest.php +++ b/tests/PhpSpreadsheetTests/Reader/SheetsXlsxChartTest.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\IOFactory; +use PhpOffice\PhpSpreadsheet\Reader\IReader; use PHPUnit\Framework\TestCase; class SheetsXlsxChartTest extends TestCase @@ -11,8 +12,8 @@ class SheetsXlsxChartTest extends TestCase public function testLoadSheetsXlsxChart(): void { $filename = 'tests/data/Reader/XLSX/sheetsChartsTest.xlsx'; - $reader = IOFactory::createReader('Xlsx')->setIncludeCharts(true); - $spreadsheet = $reader->load($filename); + $reader = IOFactory::createReader('Xlsx'); + $spreadsheet = $reader->load($filename, IReader::LOAD_WITH_CHARTS); $worksheet = $spreadsheet->getActiveSheet(); $charts = $worksheet->getChartCollection();