From 9fc8e501b84fe1da2f643d166dfa2d853f3fba6f Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:28:36 -0800 Subject: [PATCH 1/3] Extremely Limited Support for GROUPBY Function This is a partial response to issue #4282. The actual logic to implement GROUPBY is probably very complicated. And, even worse, Excel has thrown a whole new way of (internally) specifying one of the arguments into the mix. That argument is a function name, expressed not as a mapped integer (as SUBTOTAL does), nor even as a string, but as the unquoted function name prefixed by `_xleta.`. And, unlike its `_xlfn.` and `_xlws.` predecessors, it is difficult to figure out when the new prefix needs to be added, and when it needs to be ignored. I am not even going to attempt that task with this ticket. So, what does this change do? Like earlier attempts to introduce limited functionality (such as with form controls), it is there so that using GROUPBY can be passed through - you can load a spreadsheet that contains it, and save it to a new spreadsheet, and the function and its results are preserved. Some cautionary notes. Dynamic arrays must be enabled (the function makes no sense without doing that). Changing any of the inputs used in the function may result in internal inconsistencies between PhpSpreadsheet and Excel; this is especially so if the dimensions of the returned array change as a result of changes to the input data. The programmer can avoid some of these problems by changing the formulatAttributes of the cell where the function is used; this may be difficult to do in practice. Oh, yes, using the GROUPBY cell as an argument in another formula will probably lead to problems. Finally, I confess that part of this solution looks awfully kludgey to me. With its limitations and those cautions, is it worth proceeding with this change? My gut feel is that it is more useful to proceed than not. However, I will give others the opportunity to weigh in. I will wait at least a couple of weeks into the new year before proceeding with this. --- docs/references/function-list-by-category.md | 1 + docs/references/function-list-by-name.md | 1 + .../Calculation/Calculation.php | 11 +++++- src/PhpSpreadsheet/Worksheet/Worksheet.php | 6 +++- .../Writer/Xlsx/FunctionPrefix.php | 1 + src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 6 +++- .../Reader/Xlsx/GroupByLimitedTest.php | 34 ++++++++++++++++++ tests/data/Reader/XLSX/excel-groupby-one.xlsx | Bin 0 -> 13632 bytes 8 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php create mode 100644 tests/data/Reader/XLSX/excel-groupby-one.xlsx diff --git a/docs/references/function-list-by-category.md b/docs/references/function-list-by-category.md index 458a59b39c..fdd23ae624 100644 --- a/docs/references/function-list-by-category.md +++ b/docs/references/function-list-by-category.md @@ -245,6 +245,7 @@ COLUMNS | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\RowCo FILTER | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Filter::filter FORMULATEXT | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Formula::text GETPIVOTDATA | **Not yet Implemented** +GROUPBY | **Not yet Implemented** HLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup::lookup HYPERLINK | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hyperlink::set INDEX | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix::index diff --git a/docs/references/function-list-by-name.md b/docs/references/function-list-by-name.md index addc2e3e97..a403beffe8 100644 --- a/docs/references/function-list-by-name.md +++ b/docs/references/function-list-by-name.md @@ -239,6 +239,7 @@ GCD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpread GEOMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages\Mean::geometric GESTEP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\Compare::GESTEP GETPIVOTDATA | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** +GROUPBY | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented** GROWTH | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends::GROWTH ## H diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index ab52fb1af9..704332b59e 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -1256,6 +1256,11 @@ public static function getExcelConstants(string $key): bool|null 'functionCall' => [Functions::class, 'DUMMY'], 'argumentCount' => '2+', ], + 'GROUPBY' => [ + 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, + 'functionCall' => [Functions::class, 'DUMMY'], + 'argumentCount' => '3-7', + ], 'GROWTH' => [ 'category' => Category::CATEGORY_STATISTICAL, 'functionCall' => [Statistical\Trends::class, 'GROWTH'], @@ -4601,7 +4606,7 @@ private static function dataTestReference(array &$operandData): mixed private static int $matchIndex10 = 10; /** - * @return array|false + * @return array|false|string */ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell $cell = null) { @@ -5182,6 +5187,9 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) { // if the token is a named range or formula, evaluate it and push the result onto the stack $definedName = $matches[6]; + if (str_starts_with($definedName, '_xleta')) { + return Functions::NOT_YET_IMPLEMENTED; + } if ($cell === null || $pCellWorksheet === null) { return $this->raiseFormulaError("undefined name '$token'"); } @@ -5214,6 +5222,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell } $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack, $specifiedWorksheet !== ''); + if (isset($storeKey)) { $branchStore[$storeKey] = $result; } diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 546a6ffe5b..5943e5a993 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -49,6 +49,8 @@ class Worksheet public const MERGE_CELL_CONTENT_HIDE = 'hide'; public const MERGE_CELL_CONTENT_MERGE = 'merge'; + public const FUNCTION_LIKE_GROUPBY = '/\\b(groupby|_xleta)\\b/i'; // weird new syntax + protected const SHEET_NAME_REQUIRES_NO_QUOTES = '/^[_\p{L}][_\p{L}\p{N}]*$/mui'; /** @@ -3701,7 +3703,9 @@ public function calculateArrays(bool $preCalculateFormulas = true): void $keys = $this->cellCollection->getCoordinates(); foreach ($keys as $key) { if ($this->getCell($key)->getDataType() === DataType::TYPE_FORMULA) { - $this->getCell($key)->getCalculatedValue(); + if (preg_match(self::FUNCTION_LIKE_GROUPBY, $this->getCell($key)->getValue()) !== 1) { + $this->getCell($key)->getCalculatedValue(); + } } } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php b/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php index a563bd9130..16834dc04d 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php @@ -142,6 +142,7 @@ class FunctionPrefix . '|drop' . '|expand' . '|filter' + . '|groupby' . '|hstack' . '|isomitted' . '|lambda' diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 28af258297..929b4f8c09 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1578,7 +1578,11 @@ private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksh $mappedType = $pCell->getDataType(); if ($mappedType === DataType::TYPE_FORMULA) { if ($this->useDynamicArrays) { - $tempCalc = $pCell->getCalculatedValue(); + if (preg_match(PhpspreadsheetWorksheet::FUNCTION_LIKE_GROUPBY, $cellValue) === 1) { + $tempCalc = []; + } else { + $tempCalc = $pCell->getCalculatedValue(); + } if (is_array($tempCalc)) { $objWriter->writeAttribute('cm', '1'); } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php new file mode 100644 index 0000000000..a7edf6b7ed --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php @@ -0,0 +1,34 @@ +load(self::$testbook); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $spreadsheet->disconnectWorksheets(); + $reloadedSheet = $reloadedSpreadsheet->getActiveSheet(); + self::assertSame(['t' => 'array', 'ref' => 'E3:F7'], $reloadedSheet->getCell('E3')->getFormulaAttributes()); + $group = $reloadedSheet->rangeToArray('E3:F8'); + $expected = [ + ['Design', '$505,000 '], + ['Development', '$346,000 '], + ['Marketing', '$491,000 '], + ['Research', '$573,000 '], + ['Total', '$1,915,000 '], + [null, null], + ]; + self::assertSame($expected, $reloadedSheet->rangeToArray('E3:F8')); + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/excel-groupby-one.xlsx b/tests/data/Reader/XLSX/excel-groupby-one.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..dbb92b6057e6630bd54243a2e91a16fc033291c0 GIT binary patch literal 13632 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 U6a2xzke)uDpBNO*^z+yM1GEqh6aWAK literal 0 HcmV?d00001 From 52c6a67af4c58e00eb66712e376a7d7c8e7e05bc Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:08:42 -0800 Subject: [PATCH 2/3] Eliminate Unused Statement --- tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php index a7edf6b7ed..e41b2190ed 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/GroupByLimitedTest.php @@ -19,7 +19,6 @@ public function testRowBreaks(): void $spreadsheet->disconnectWorksheets(); $reloadedSheet = $reloadedSpreadsheet->getActiveSheet(); self::assertSame(['t' => 'array', 'ref' => 'E3:F7'], $reloadedSheet->getCell('E3')->getFormulaAttributes()); - $group = $reloadedSheet->rangeToArray('E3:F8'); $expected = [ ['Design', '$505,000 '], ['Development', '$346,000 '], From bfcfaeea8ed27fa1a495d6752ec0ec640898c3d5 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:04:47 -0800 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d78534a3e..51defbef9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Xlsx Reader Shared Formula with Boolean Result. Partial solution for [Issue #4280](https://github.com/PHPOffice/PhpSpreadsheet/issues/4280) [PR #4281](https://github.com/PHPOffice/PhpSpreadsheet/pull/4281) - Retitling cloned Worksheets. [Issue #641](https://github.com/PHPOffice/PhpSpreadsheet/issues/641) [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302) +- Extremely limited support for GROUPBY function. Partial response to [Issue #4282](https://github.com/PHPOffice/PhpSpreadsheet/issues/4282) [PR #4283](https://github.com/PHPOffice/PhpSpreadsheet/pull/4283) ## 2024-12-26 - 3.7.0