Fix VLOOKUP #N/A with whole-column ranges when end column has no data

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.
This commit is contained in:
DIReports
2026-08-19 16:25:33 +02:00
parent 930293a32a
commit 5e91ce2ba2
2 changed files with 58 additions and 1 deletions
@@ -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;
@@ -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
{