Merge pull request #2751 from PHPOffice/CalcEngine-Bugfix-Row-Column-Ranges

Resolve Calculation Engine bug with row and column ranges being identified as named ranges
This commit is contained in:
Mark Baker 2022-04-15 15:10:38 +02:00 committed by GitHub
commit de173d4705
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 1 deletions

View File

@ -4178,7 +4178,10 @@ class Calculation
$testPrevOp = $stack->last(1);
if ($testPrevOp !== null && $testPrevOp['value'] === ':') {
$stackItemType = 'Cell Reference';
if (
!is_numeric($val) &&
((ctype_alpha($val) === false || strlen($val) > 3)) &&
(preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false) &&
($this->spreadsheet->getNamedRange($val) !== null)
) {

View File

@ -3,6 +3,8 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
class ParseFormulaTest extends TestCase
@ -12,7 +14,11 @@ class ParseFormulaTest extends TestCase
*/
public function testParseOperations(array $expectedStack, string $formula): void
{
$parser = Calculation::getInstance();
$spreadsheet = new Spreadsheet();
$spreadsheet->addNamedRange(new NamedRange('GROUP1', $spreadsheet->getActiveSheet(), 'B2:D4'));
$spreadsheet->addNamedRange(new NamedRange('GROUP2', $spreadsheet->getActiveSheet(), 'D4:F6'));
$parser = Calculation::getInstance($spreadsheet);
$stack = $parser->parseFormula($formula);
self::assertSame($expectedStack, $stack);
}
@ -80,6 +86,36 @@ class ParseFormulaTest extends TestCase
],
'=-DEFINED_NAME%',
],
'Integer Numbers with Operator' => [
[
['type' => 'Value', 'value' => 2, 'reference' => null],
['type' => 'Value', 'value' => 3, 'reference' => null],
['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
],
'=2*3',
],
'Float Numbers with Operator' => [
[
['type' => 'Value', 'value' => 2.5, 'reference' => null],
['type' => 'Value', 'value' => 3.5, 'reference' => null],
['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
],
'=2.5*3.5',
],
'Strings with Operator' => [
[
['type' => 'Value', 'value' => '"HELLO"', 'reference' => null],
['type' => 'Value', 'value' => '"WORLD"', 'reference' => null],
['type' => 'Binary Operator', 'value' => '&', 'reference' => null],
],
'="HELLO"&"WORLD"',
],
'Error' => [
[
['type' => 'Value', 'value' => '#DIV0!', 'reference' => null],
],
'=#DIV0!',
],
'Cell Range' => [
[
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
@ -88,6 +124,16 @@ class ParseFormulaTest extends TestCase
],
'=A1:C3',
],
'Chained Cell Range' => [
[
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
['type' => 'Cell Reference', 'value' => 'C3', 'reference' => 'C3'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
['type' => 'Cell Reference', 'value' => 'E5', 'reference' => 'E5'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
],
'=A1:C3:E5',
],
'Cell Range Intersection' => [
[
['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],
@ -100,6 +146,40 @@ class ParseFormulaTest extends TestCase
],
'=A1:C3 B2:D4',
],
'Row Range' => [
[
['type' => 'Row Reference', 'value' => 'A2', 'reference' => 'A2'],
['type' => 'Row Reference', 'value' => 'XFD3', 'reference' => 'XFD3'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
],
'=2:3',
],
'Column Range' => [
[
['type' => 'Column Reference', 'value' => 'B1', 'reference' => 'B1'],
['type' => 'Column Reference', 'value' => 'C1048576', 'reference' => 'C1048576'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
],
'=B:C',
],
'Range with Defined Names' => [
[
['type' => 'Defined Name', 'value' => 'GROUP1', 'reference' => 'GROUP1'],
['type' => 'Defined Name', 'value' => 'D4', 'reference' => 'GROUP2'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
['type' => 'Defined Name', 'value' => 'F6', 'reference' => 'GROUP2'],
['type' => 'Binary Operator', 'value' => ':', 'reference' => null],
],
'=GROUP1:GROUP2',
],
'Named Range with Binary Operator' => [
[
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_2', 'reference' => 'DEFINED_NAME_2'],
['type' => 'Binary Operator', 'value' => '/', 'reference' => null],
],
'=DEFINED_NAME_1/DEFINED_NAME_2',
],
'Named Range Intersection' => [
[
['type' => 'Defined Name', 'value' => 'DEFINED_NAME_1', 'reference' => 'DEFINED_NAME_1'],
@ -108,6 +188,22 @@ class ParseFormulaTest extends TestCase
],
'=DEFINED_NAME_1 DEFINED_NAME_2',
],
// 'Structured Reference Arithmetic' => [
// [
// ['type' => 'Structured Reference', 'value' => '[@Quantity]', 'reference' => null],
// ['type' => 'Structured Reference', 'value' => '[@[Unit Price]]', 'reference' => null],
// ['type' => 'Binary Operator', 'value' => '*', 'reference' => null],
// ],
// '=[@Quantity]*[@[Unit Price]]',
// ],
// 'Structured Reference Intersection' => [
// [
// ['type' => 'Structured Reference', 'value' => 'DeptSales[[Sales Person]:[Sales Amount]]', 'reference' => null],
// ['type' => 'Structured Reference', 'value' => 'DeptSales[[Region]:[% Commission]]', 'reference' => null],
// ['type' => 'Binary Operator', 'value' => '∩', 'reference' => null],
// ],
// '=DeptSales[[Sales Person]:[Sales Amount]] DeptSales[[Region]:[% Commission]]',
// ],
// 'Cell Range Union' => [
// [
// ['type' => 'Cell Reference', 'value' => 'A1', 'reference' => 'A1'],