diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php index c3643cd65..69974ff58 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php @@ -103,7 +103,7 @@ class Offset $sheetName = ''; if (str_contains($cellAddress, '!')) { - [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true, true); + [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true); } $worksheet = ($sheetName !== '') diff --git a/src/PhpSpreadsheet/Worksheet/Validations.php b/src/PhpSpreadsheet/Worksheet/Validations.php index 303e016ea..4a8720dbb 100644 --- a/src/PhpSpreadsheet/Worksheet/Validations.php +++ b/src/PhpSpreadsheet/Worksheet/Validations.php @@ -110,7 +110,7 @@ class Validations return (string) $cellRange; } - public static function definedNameToCoordinate(string $coordinate, Worksheet $worksheet, bool $replaceDollar = false): string + public static function definedNameToCoordinate(string $coordinate, Worksheet $worksheet): string { // Uppercase coordinate $coordinate = strtoupper($coordinate); @@ -120,9 +120,6 @@ class Validations if ($defined !== null) { if ($defined->getWorksheet() === $worksheet && !$defined->isFormula()) { $coordinate = Preg::replace('/^=/', '', $defined->getValue()); - if ($replaceDollar) { - $coordinate = str_replace('$', '', $coordinate); - } } } diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 2577044de..b992b5e88 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -1396,9 +1396,10 @@ class Worksheet public function getStyle(AddressRange|CellAddress|int|string|array $cellCoordinate): Style { if (is_string($cellCoordinate)) { - $cellCoordinate = Validations::definedNameToCoordinate($cellCoordinate, $this, true); + $cellCoordinate = Validations::definedNameToCoordinate($cellCoordinate, $this); } $cellCoordinate = Validations::validateCellOrCellRange($cellCoordinate); + $cellCoordinate = str_replace('$', '', $cellCoordinate); // set this sheet as active $this->getParentOrThrow()->setActiveSheetIndex($this->getParentOrThrow()->getIndex($this)); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsRefTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsRefTest.php index da977cf91..939aa3e33 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsRefTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsRefTest.php @@ -6,46 +6,41 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information; use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef\AllSetupTeardown; +use PHPUnit\Framework\Attributes\DataProvider; class IsRefTest extends AllSetupTeardown { - private bool $skipA13 = true; - - public function testIsRef(): void + #[DataProvider('providerIsRef')] + public function testIsRef(mixed $expected, string $ref): void { + if ($expected === 'incomplete') { + self::markTestIncomplete('Calculation is too complicated'); + } $sheet = $this->getSheet(); $sheet->getParentOrThrow()->addDefinedName(new NamedRange('NAMED_RANGE', $sheet, 'C1')); + $sheet->getCell('A1')->setValue("=ISREF($ref)"); + self::assertSame($expected, $sheet->getCell('A1')->getCalculatedValue()); + } - $sheet->getCell('A1')->setValue('=ISREF(B1)'); - $sheet->getCell('A2')->setValue('=ISREF(B1:B2)'); - $sheet->getCell('A3')->setValue('=ISREF(B1:D4 C1:C5)'); - $sheet->getCell('A4')->setValue('=ISREF("PHP")'); - $sheet->getCell('A5')->setValue('=ISREF(B1*B2)'); - $sheet->getCell('A6')->setValue('=ISREF(Worksheet2!B1)'); - $sheet->getCell('A7')->setValue('=ISREF(NAMED_RANGE)'); - $sheet->getCell('A8')->setValue('=ISREF(INDIRECT("' . $sheet->getTitle() . '" & "!" & "A1"))'); - $sheet->getCell('A9')->setValue('=ISREF(INDIRECT("A1"))'); - $sheet->getCell('A10')->setValue('=ISREF(INDIRECT("Invalid Worksheet" & "!" & "A1"))'); - $sheet->getCell('A11')->setValue('=ISREF(INDIRECT("Invalid Worksheet" & "!A1"))'); - $sheet->getCell('A12')->setValue('=ISREF(ZZZ1)'); - $sheet->getCell('A13')->setValue('=ISREF(CHOOSE(2, A1, B1, C1))'); - - self::assertTrue($sheet->getCell('A1')->getCalculatedValue()); // Cell Reference - self::assertTrue($sheet->getCell('A2')->getCalculatedValue()); // Cell Range - self::assertTrue($sheet->getCell('A3')->getCalculatedValue()); // Complex Cell Range - self::assertFalse($sheet->getCell('A4')->getCalculatedValue()); // Text String - self::assertFalse($sheet->getCell('A5')->getCalculatedValue()); // Result of a math expression - self::assertTrue($sheet->getCell('A6')->getCalculatedValue()); // Cell Reference with worksheet - self::assertTrue($sheet->getCell('A7')->getCalculatedValue()); // Named Range - self::assertTrue($sheet->getCell('A8')->getCalculatedValue()); // Indirect to a Cell Reference - self::assertTrue($sheet->getCell('A9')->getCalculatedValue()); // Indirect to a Worksheet/Cell Reference - self::assertFalse($sheet->getCell('A10')->getCalculatedValue()); // Indirect to an Invalid Worksheet/Cell Reference - self::assertFalse($sheet->getCell('A11')->getCalculatedValue()); // Indirect to an Invalid Worksheet/Cell Reference - self::assertFalse($sheet->getCell('A12')->getCalculatedValue()); // Invalid Cell Reference - if ($this->skipA13) { - self::markTestIncomplete('Calculation for A13 is too complicated'); - } - self::assertTrue($sheet->getCell('A13')->getCalculatedValue()); // returned Cell Reference + public static function providerIsRef(): array + { + return [ + 'cell reference' => [true, 'B1'], + 'invalid cell reference' => [false, 'ZZZ1'], + 'cell range' => [true, 'B1:B2'], + 'complex cell range' => [true, 'B1:D4 C1:C5'], + 'text string' => [false, '"PHP"'], + 'math expression' => [false, 'B1*B2'], + 'unquoted sheet name' => [true, 'Worksheet2!B1'], + 'quoted sheet name' => [true, "'Worksheet2'!B1:B2"], + 'quoted sheet name with apostrophe' => [true, "'Work''sheet2'!B1:B2"], + 'named range' => [true, 'NAMED_RANGE'], + 'unknown named range' => ['#NAME?', 'xNAMED_RANGE'], + 'indirect to a cell reference' => [true, 'INDIRECT("A1")'], + 'indirect to a worksheet/cell reference' => [true, 'INDIRECT("\'Worksheet\'!A1")'], + 'indirect to invalid worksheet/cell reference' => [false, 'INDIRECT("\'Invalid Worksheet\'!A1")'], + 'returned cell reference' => ['incomplete', 'CHOOSE(2, A1, B1, C1)'], + ]; } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/ColumnOnSpreadsheetTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/ColumnOnSpreadsheetTest.php index a57b91eb3..d019d428b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/ColumnOnSpreadsheetTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/ColumnOnSpreadsheetTest.php @@ -54,4 +54,22 @@ class ColumnOnSpreadsheetTest extends AllSetupTeardown $result = $sheet->getCell('B3')->getCalculatedValue(); self::assertSame('#NAME?', $result); } + + public function testCOLUMNSheetWithApostrophe(): void + { + $this->setArrayAsValue(); + $sheet = $this->getSheet(); + + $sheet1 = $this->getSpreadsheet()->createSheet(); + $sheet1->setTitle("apo''strophe"); + $this->getSpreadsheet()->addNamedRange(new NamedRange('newnr', $sheet1, '$F$5:$H$5', true)); // defined locally, only usable on sheet1 + + $sheet1->getCell('B3')->setValue('=COLUMN(newnr)'); + $result = $sheet1->getCell('B3')->getCalculatedValue(); + self::assertSame(6, $result); + + $sheet->getCell('B3')->setValue('=COLUMN(newnr)'); + $result = $sheet->getCell('B3')->getCalculatedValue(); + self::assertSame('#NAME?', $result); + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/OffsetTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/OffsetTest.php index c6a66c4c2..5a6bb93ae 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/OffsetTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/OffsetTest.php @@ -6,10 +6,11 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef; use PhpOffice\PhpSpreadsheet\Calculation\LookupRef; use PhpOffice\PhpSpreadsheet\NamedRange; +use PHPUnit\Framework\Attributes\DataProvider; class OffsetTest extends AllSetupTeardown { - #[\PHPUnit\Framework\Attributes\DataProvider('providerOFFSET')] + #[DataProvider('providerOFFSET')] public function testOFFSET(mixed $expectedResult, null|string $cellReference = null): void { $result = LookupRef\Offset::OFFSET($cellReference); @@ -58,4 +59,19 @@ class OffsetTest extends AllSetupTeardown self::assertSame(2, $workSheet->getCell('B2')->getCalculatedValue()); } + + public function testOffsetNamedRangeApostropheSheet(): void + { + $workSheet = $this->getSheet(); + $workSheet->setTitle("apo'strophe"); + $workSheet->setCellValue('A1', 1); + $workSheet->setCellValue('A2', 2); + + $this->getSpreadsheet()->addNamedRange(new NamedRange('demo', $workSheet, '=$A$1')); + + $workSheet->setCellValue('B1', '=demo'); + $workSheet->setCellValue('B2', '=OFFSET(demo, 1, 0)'); + + self::assertSame(2, $workSheet->getCell('B2')->getCalculatedValue()); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php index c3d3655e9..2c3a36698 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php @@ -31,6 +31,28 @@ class DefinedNamesTest extends TestCase $spreadsheet->disconnectWorksheets(); } + public function testDefinedNamesApostropheValue(): void + { + $filename = 'tests/data/Reader/Ods/DefinedNames.apostrophe.ods'; + $reader = new Ods(); + $spreadsheet = $reader->load($filename); + $calculation = Calculation::getInstance($spreadsheet); + $calculation->setInstanceArrayReturnType( + Calculation::RETURN_ARRAY_AS_VALUE + ); + $worksheet = $spreadsheet->getActiveSheet(); + self::assertSame("apo'strophe", $worksheet->getTitle()); + + $firstDefinedNameValue = $worksheet->getCell('First')->getValue(); + $secondDefinedNameValue = $worksheet->getCell('Second')->getValue(); + $calculatedFormulaValue = $worksheet->getCell('B2')->getCalculatedValue(); + + self::assertSame(3, $firstDefinedNameValue); + self::assertSame(4, $secondDefinedNameValue); + self::assertSame(12, $calculatedFormulaValue); + $spreadsheet->disconnectWorksheets(); + } + public function testDefinedNamesArray(): void { $filename = 'tests/data/Reader/Ods/DefinedNames.ods'; diff --git a/tests/data/Reader/Ods/DefinedNames.apostrophe.ods b/tests/data/Reader/Ods/DefinedNames.apostrophe.ods new file mode 100644 index 000000000..8b4642add Binary files /dev/null and b/tests/data/Reader/Ods/DefinedNames.apostrophe.ods differ