From ae4df820bf83ce7a170ddee12067ba035f900b76 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 10 Jul 2025 18:11:41 -0700 Subject: [PATCH] Xlsx Reader Use Dynamic Arrays if Spreadsheet Did So By default, when an Xlsx spreadsheet is read, its calculation engine is set to `RETURN_ARRAY_AS_VALUE` for array formulas. The user does have the option to change this. However, the presence of certain metadata in the file indicates that it was saved as if `RETURN_ARRAY_AS_ARRAY` was in use. This PR tests for the metadata, and, if present, sets `...ARRAY` rather than `...VALUE`. This eliminates the need for any extra action on the user's part. --- src/PhpSpreadsheet/Reader/Xlsx.php | 10 +++ .../Reader/Xlsx/ReadDynamTest.php | 71 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/ReadDynamTest.php diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index b3e133f783..7daed146ea 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; @@ -449,6 +450,15 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet $relTarget = substr($relTarget, 4); } switch ($rel['Type']) { + case "$xmlNamespaceBase/sheetMetadata": + if ($this->fileExistsInArchive($zip, "xl/{$relTarget}")) { + $excel->getCalculationEngine() + ?->setInstanceArrayReturnType( + Calculation::RETURN_ARRAY_AS_ARRAY + ); + } + + break; case "$xmlNamespaceBase/theme": if (!$this->fileExistsInArchive($zip, "xl/{$relTarget}")) { break; // issue3770 diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/ReadDynamTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/ReadDynamTest.php new file mode 100644 index 0000000000..8ad7053f1f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/ReadDynamTest.php @@ -0,0 +1,71 @@ +setUseCSEArrays(true); + } + + public function testCse(): void + { + $spreadsheetOld = new Spreadsheet(); + $sheetOld = $spreadsheetOld->getActiveSheet(); + $calcOld = Calculation::getInstance($spreadsheetOld); + $calcOld->setInstanceArrayReturnType( + Calculation::RETURN_ARRAY_AS_ARRAY + ); + $sheetOld->fromArray( + [1, 2, 2, 4, 3, 2, 1, 3, 3, 3, 5], + null, + 'A14', + true + ); + $sheetOld->setCellValue('A15', '=UNIQUE(A14:K14, TRUE)'); + /** @var callable */ + $callableWriter = [$this, 'writeCse']; + $spreadsheet = $this->writeAndReload($spreadsheetOld, 'Xlsx', null, $callableWriter); + $spreadsheetOld->disconnectWorksheets(); + $calc = Calculation::getInstance($spreadsheet); + self::assertSame( + Calculation::RETURN_ARRAY_AS_VALUE, + $calc->getInstanceArrayReturnType() + ); + $spreadsheet->disconnectWorksheets(); + } + + public function testDynam(): void + { + $spreadsheetOld = new Spreadsheet(); + $sheetOld = $spreadsheetOld->getActiveSheet(); + $calcOld = Calculation::getInstance($spreadsheetOld); + $calcOld->setInstanceArrayReturnType( + Calculation::RETURN_ARRAY_AS_ARRAY + ); + $sheetOld->fromArray( + [1, 2, 2, 4, 3, 2, 1, 3, 3, 3, 5], + null, + 'A14', + true + ); + $sheetOld->setCellValue('A15', '=UNIQUE(A14:K14, TRUE)'); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheetOld, 'Xlsx'); + $spreadsheet = $this->writeAndReload($spreadsheetOld, 'Xlsx'); + $spreadsheetOld->disconnectWorksheets(); + $calc = Calculation::getInstance($spreadsheet); + self::assertSame( + Calculation::RETURN_ARRAY_AS_ARRAY, + $calc->getInstanceArrayReturnType() + ); + $spreadsheet->disconnectWorksheets(); + } +}