From 5e91ce2ba2a97d17a005ec129f031259b2eaa1b5 Mon Sep 17 00:00:00 2001 From: DIReports Date: Wed, 19 Aug 2026 16:25:33 +0200 Subject: [PATCH] Fix VLOOKUP #N/A with whole-column ranges when end column has no data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When resolving the end reference of a whole-column range (e.g. $A:$F), getHighestDataRow($col) was called for the specific end column. If that column contains no data it returns 1, producing an inverted range such as A4:F1. This caused VLOOKUP and similar functions to return #N/A when the formula used a whole-column reference across sheets where data only exists in the left-hand columns. Fix: call getHighestDataRow() without a column argument so the overall highest data row across all columns is used for the end reference. Reproducer: Sheet2!A:D has data in cols A–C only; $A:$F produced A4:F1 → #N/A. After fix: A1:F4 → VLOOKUP finds the value correctly. --- .../Calculation/Calculation.php | 7 ++- .../Calculation/RowColumnReferenceTest.php | 52 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 7e6e18d3a..8dbc0233e 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -1544,7 +1544,12 @@ class Calculation extends CalculationLocale } elseif (ctype_alpha($val) && strlen($val) <= 3) { // Column range $stackItemType = 'Column Reference'; - $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : AddressRange::MAX_ROW; // Max 1,048,576 rows for Excel2007 + // Use getHighestDataRow() without a column argument so that the overall + // highest row (across all columns) is used for the end reference. + // Using getHighestDataRow($val) for the specific end column is incorrect + // when that column contains no data: it returns 1, producing an inverted + // range such as A4:F1 for whole-column references like $A:$F. + $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow() : AddressRange::MAX_ROW; // Max 1,048,576 rows for Excel2007 $val = "{$rangeWS2}{$val}{$endRowColRef}"; } $stackItemReference = $val; diff --git a/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php b/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php index 1a1cfa88f..7b86463ef 100644 --- a/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php @@ -35,6 +35,58 @@ class RowColumnReferenceTest extends TestCase $this->spreadSheet->setActiveSheetIndexByName('summary sheet'); } + /** + * VLOOKUP with a whole-column range where the end column contains no data. + * getHighestDataRow() on the empty end column returned 1, producing an + * inverted range (e.g. A4:F1) that caused a #N/A result. + * + * @see https://github.com/PHPOffice/PhpSpreadsheet/issues/XXXX + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerVlookupWholeColumnRange')] + public function testVlookupWholeColumnRange(string $formula, string $expectedResult): void + { + $spreadsheet = new Spreadsheet(); + + // Lookup sheet: col A = keys, col C = values; cols B, D, E, F are empty + $lookupSheet = new Worksheet($spreadsheet, 'Sheet2'); + $spreadsheet->addSheet($lookupSheet, 0); + $lookupSheet->fromArray([ + [1234, null, 'row1'], + [2345, null, 'row2'], + [3456, null, 'row3'], + [4567, null, 'row4'], + ], null, 'A1'); + + // Formula sheet: C1 holds the lookup value + $formulaSheet = new Worksheet($spreadsheet, 'Sheet1'); + $spreadsheet->addSheet($formulaSheet, 1); + $formulaSheet->setCellValue('C1', 3456); + $formulaSheet->setCellValue('A1', $formula); + $spreadsheet->setActiveSheetIndexByName('Sheet1'); + + $result = $formulaSheet->getCell('A1')->getCalculatedValue(); + self::assertSame($expectedResult, $result); + $spreadsheet->disconnectWorksheets(); + } + + public static function providerVlookupWholeColumnRange(): array + { + return [ + 'VLOOKUP with absolute whole-column range across sheets' => [ + '=VLOOKUP($C1,Sheet2!$A:$F,3,FALSE)', + 'row3', + ], + 'VLOOKUP with relative whole-column range across sheets' => [ + '=VLOOKUP($C1,Sheet2!A:F,3,FALSE)', + 'row3', + ], + 'VLOOKUP with mixed whole-column range across sheets' => [ + '=VLOOKUP($C1,Sheet2!A:$F,3,FALSE)', + 'row3', + ], + ]; + } + #[\PHPUnit\Framework\Attributes\DataProvider('providerCurrentWorksheetFormulae')] public function testCurrentWorksheet(string $formula, float $expectedResult): void {