Skip to content

Commit 716964e

Browse files
author
MarkBaker
committed
Resolve Calculation Engine bug with row and column ranges being identified as named ranges, adding overhead with the additional validation to process that named range
1 parent 4a28fd6 commit 716964e

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
lines changed

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4178,7 +4178,10 @@ private function internalParseFormula($formula, ?Cell $cell = null)
41784178
$testPrevOp = $stack->last(1);
41794179
if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
41804180
$stackItemType = 'Cell Reference';
4181+
41814182
if (
4183+
!is_numeric($val) &&
4184+
((ctype_alpha($val) === false || strlen($val) > 3)) &&
41824185
(preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false) &&
41834186
($this->spreadsheet->getNamedRange($val) !== null)
41844187
) {

tests/PhpSpreadsheetTests/Calculation/ParseFormulaTest.php

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
44

55
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6+
use PhpOffice\PhpSpreadsheet\NamedRange;
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
68
use PHPUnit\Framework\TestCase;
79

810
class ParseFormulaTest extends TestCase
@@ -12,7 +14,11 @@ class ParseFormulaTest extends TestCase
1214
*/
1315
public function testParseOperations(array $expectedStack, string $formula): void
1416
{
15-
$parser = Calculation::getInstance();
17+
$spreadsheet = new Spreadsheet();
18+
$spreadsheet->addNamedRange(new NamedRange('GROUP1', $spreadsheet->getActiveSheet(), 'B2:D4'));
19+
$spreadsheet->addNamedRange(new NamedRange('GROUP2', $spreadsheet->getActiveSheet(), 'D4:F6'));
20+
21+
$parser = Calculation::getInstance($spreadsheet);
1622
$stack = $parser->parseFormula($formula);
1723
self::assertSame($expectedStack, $stack);
1824
}
@@ -80,6 +86,36 @@ public function providerBinaryOperations(): array
8086
],
8187
'=-DEFINED_NAME%',
8288
],
89+
'Integer Numbers with Operator' => [
90+
[
91+
['type' => 'Value', 'value' => 2, 'reference' => null],
92+
['type' => 'Value', 'value' => 3, 'reference' => null],
93+
['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
94+
],
95+
'=2*3',
96+
],
97+
'Float Numbers with Operator' => [
98+
[
99+
['type' => 'Value', 'value' => 2.5, 'reference' => null],
100+
['type' => 'Value', 'value' => 3.5, 'reference' => null],
101+
['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
102+
],
103+
'=2.5*3.5',
104+
],
105+
'Strings with Operator' => [
106+
[
107+
['type' => 'Value', 'value' => '"HELLO"', 'reference' => null],
108+
['type' => 'Value', 'value' => '"WORLD"', 'reference' => null],
109+
['type' => 'Binary Operator', 'value' => '&', 'reference' => null],
110+
],
111+
'="HELLO"&"WORLD"',
112+
],
113+
'Error' => [
114+
[
115+
['type' => 'Value', 'value' => '#DIV0!', 'reference' => null],
116+
],
117+
'=#DIV0!',
118+
],
83119
'Cell Range' => [
84120
[
85121
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
@@ -88,6 +124,16 @@ public function providerBinaryOperations(): array
88124
],
89125
'=A1:C3',
90126
],
127+
'Chained Cell Range' => [
128+
[
129+
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
130+
['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
131+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
132+
['type' => 'Cell Reference', 'value' => 'E5', 'reference' => 'E5'],
133+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
134+
],
135+
'=A1:C3:E5',
136+
],
91137
'Cell Range Intersection' => [
92138
[
93139
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
@@ -100,6 +146,40 @@ public function providerBinaryOperations(): array
100146
],
101147
'=A1:C3 B2:D4',
102148
],
149+
'Row Range' => [
150+
[
151+
['type' => 'Row Reference', 'value' => 'A2', 'reference' => 'A2'],
152+
['type' => 'Row Reference', 'value' => 'XFD3', 'reference' => 'XFD3'],
153+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
154+
],
155+
'=2:3',
156+
],
157+
'Column Range' => [
158+
[
159+
['type' => 'Column Reference', 'value' => 'B1', 'reference' => 'B1'],
160+
['type' => 'Column Reference', 'value' => 'C1048576', 'reference' => 'C1048576'],
161+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
162+
],
163+
'=B:C',
164+
],
165+
'Range with Defined Names' => [
166+
[
167+
['type' => 'Defined Name', 'value' => 'GROUP1', 'reference' => 'GROUP1'],
168+
['type' => 'Defined Name', 'value' => 'D4', 'reference' => 'GROUP2'],
169+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
170+
['type' => 'Defined Name', 'value' => 'F6', 'reference' => 'GROUP2'],
171+
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
172+
],
173+
'=GROUP1:GROUP2',
174+
],
175+
'Named Range with Binary Operator' => [
176+
[
177+
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
178+
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_2', 'reference' => 'DEFINED_NAME_2'],
179+
['type' => 'Binary Operator', 'value' => '/', 'reference' => null],
180+
],
181+
'=DEFINED_NAME_1/DEFINED_NAME_2',
182+
],
103183
'Named Range Intersection' => [
104184
[
105185
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
@@ -108,6 +188,22 @@ public function providerBinaryOperations(): array
108188
],
109189
'=DEFINED_NAME_1 DEFINED_NAME_2',
110190
],
191+
// 'Structured Reference Arithmetic' => [
192+
// [
193+
// ['type' => 'Structured Reference', 'value' => '[@Quantity]', 'reference' => null],
194+
// ['type' => 'Structured Reference', 'value' => '[@[Unit Price]]', 'reference' => null],
195+
// ['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
196+
// ],
197+
// '=[@Quantity]*[@[Unit Price]]',
198+
// ],
199+
// 'Structured Reference Intersection' => [
200+
// [
201+
// ['type' => 'Structured Reference', 'value' => 'DeptSales[[Sales Person]:[Sales Amount]]', 'reference' => null],
202+
// ['type' => 'Structured Reference', 'value' => 'DeptSales[[Region]:[% Commission]]', 'reference' => null],
203+
// ['type' => 'Binary Operator', 'value' => '∩', 'reference' => null],
204+
// ],
205+
// '=DeptSales[[Sales Person]:[Sales Amount]] DeptSales[[Region]:[% Commission]]',
206+
// ],
111207
// 'Cell Range Union' => [
112208
// [
113209
// ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],

0 commit comments

Comments
 (0)