diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 6de546fa29..a80d61e430 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -4364,8 +4364,12 @@ private function internalParseFormula($formula, ?Cell $cell = null) $rangeStartCellRef = $output[count($output) - 2]['value'] ?? ''; } preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches); - if ($rangeStartMatches[2] > '') { - $val = $rangeStartMatches[2] . '!' . $val; + if (array_key_exists(2, $rangeStartMatches)) { + if ($rangeStartMatches[2] > '') { + $val = $rangeStartMatches[2] . '!' . $val; + } + } else { + $val = Information\ExcelError::REF(); } } else { $rangeStartCellRef = $output[count($output) - 1]['value'] ?? ''; @@ -4434,6 +4438,8 @@ private function internalParseFormula($formula, ?Cell $cell = null) } $val = $address; } + } elseif ($val === Information\ExcelError::REF()) { + $stackItemReference = $val; } else { $startRowColRef = $output[count($output) - 1]['value'] ?? ''; [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true); @@ -4793,7 +4799,7 @@ private function processTokenStack($tokens, $cellID = null, ?Cell $cell = null) } } } - if (strpos($operand1Data['reference'], '!') !== false) { + if (strpos($operand1Data['reference'] ?? '', '!') !== false) { [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true); } else { $sheet1 = ($pCellWorksheet !== null) ? $pCellWorksheet->getTitle() : ''; @@ -4830,10 +4836,21 @@ private function processTokenStack($tokens, $cellID = null, ?Cell $cell = null) $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference'])); $oCol = $oRow = []; + $breakNeeded = false; foreach ($oData as $oDatum) { - $oCR = Coordinate::coordinateFromString($oDatum); - $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1; - $oRow[] = $oCR[1]; + try { + $oCR = Coordinate::coordinateFromString($oDatum); + $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1; + $oRow[] = $oCR[1]; + } catch (\Exception $e) { + $stack->push('Error', Information\ExcelError::REF(), null); + $breakNeeded = true; + + break; + } + } + if ($breakNeeded) { + break; } $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); if ($pCellParent !== null && $this->spreadsheet !== null) { diff --git a/tests/PhpSpreadsheetTests/RefRangeTest.php b/tests/PhpSpreadsheetTests/RefRangeTest.php new file mode 100644 index 0000000000..57dbd87e50 --- /dev/null +++ b/tests/PhpSpreadsheetTests/RefRangeTest.php @@ -0,0 +1,45 @@ +getActiveSheet(); + $sheet->getCell('A1')->setValue("=SUM($rangeString)"); + self::assertSame($expectedResult, $sheet->getCell('A1')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function providerRefRange(): array + { + return [ + 'normal range' => [0, 'B1:B2'], + 'ref as end of range' => ['#REF!', 'B1:#REF!'], + 'ref as start of range' => ['#REF!', '#REF!:B2'], + 'ref as both parts of range' => ['#REF!', '#REF!:#REF!'], + 'using indirect for ref' => ['#REF!', 'B1:INDIRECT("XYZ")'], + ]; + } + + public function testRefRangeRead(): void + { + $reader = new Xlsx(); + $spreadsheet = $reader->load('tests/data/Reader/XLSX/issue.3453.xlsx'); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(0, $sheet->getCell('H1')->getCalculatedValue()); + self::assertSame('#REF!', $sheet->getCell('H2')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/issue.3453.xlsx b/tests/data/Reader/XLSX/issue.3453.xlsx new file mode 100644 index 0000000000..ec3591b5f5 Binary files /dev/null and b/tests/data/Reader/XLSX/issue.3453.xlsx differ