forked from PHPOffice/PhpSpreadsheet
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improved Support for INDIRECT, ROW, and COLUMN Functions (PHPOffice#2004
) * Improved Support for INDIRECT, ROW, and COLUMN Functions This should address issues PHPOffice#1913 and PHPOffice#1993. INDIRECT had heretofore not supported an optional parameter intended to support addresses in R1C1 format which was introduced with Excel 2010. It also had not supported the use of defined names as an argument. This PR is a replacement for PHPOffice#1995, which is currently in draft status and which I will close in a day or two. The ROW and COLUMN functions also should support defined names. I have added that, and test cases, with the latest push. ROWS and COLUMNS already supported it correctly, but there had been no test cases. Because ROW and COLUMN can return arrays, and PhpSpreadsheet does not support dynamic arrays, I left the existing direct-call tests unchanged to demonstrate those capabilities. The unit tests for INDIRECT had used mocking, and were sorely lacking (tested only error conditions). They have been replaced with normal, and hopefully adequate, tests. This includes testing globally defined names, as well as locally defined names, both in and out of scope. The test case in 1913 was too complicated for me to add as a unit test. The main impediments to it are now removed, and its complex situation will, I hope, be corrected with this fix. INDIRECT can also support a reference of the form Sheetname!localName when localName on its own would be out of scope. That functionality is added. It is also added, in theory, for ROW and COLUMN, however such a construction is rejected by the Calculation engine before passing control to ROW or COLUMN. It might be possible to change the engine to allow this, and I may want to look into that later, but it seems much too risky, and not nearly useful enough, to attempt to address that as part of this change. Several unusual test cases (leading equals sign, not-quite-as-expected name definition in file, complex indirection involving concatenation and a dropdown list) were suggested by @MarkBaker and are included in this request.
- Loading branch information
Showing
22 changed files
with
724 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?php | ||
|
||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef; | ||
|
||
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper; | ||
use PhpOffice\PhpSpreadsheet\Cell\Cell; | ||
use PhpOffice\PhpSpreadsheet\DefinedName; | ||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; | ||
|
||
class Helpers | ||
{ | ||
public const CELLADDRESS_USE_A1 = true; | ||
|
||
public const CELLADDRESS_USE_R1C1 = false; | ||
|
||
private static function convertR1C1(string &$cellAddress1, ?string &$cellAddress2, bool $a1): string | ||
{ | ||
if ($a1 === self::CELLADDRESS_USE_R1C1) { | ||
$cellAddress1 = AddressHelper::convertToA1($cellAddress1); | ||
if ($cellAddress2) { | ||
$cellAddress2 = AddressHelper::convertToA1($cellAddress2); | ||
} | ||
} | ||
|
||
return $cellAddress1 . ($cellAddress2 ? ":$cellAddress2" : ''); | ||
} | ||
|
||
private static function adjustSheetTitle(string &$sheetTitle, ?string $value): void | ||
{ | ||
if ($sheetTitle) { | ||
$sheetTitle .= '!'; | ||
if (stripos($value ?? '', $sheetTitle) === 0) { | ||
$sheetTitle = ''; | ||
} | ||
} | ||
} | ||
|
||
public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = ''): array | ||
{ | ||
$cellAddress1 = $cellAddress; | ||
$cellAddress2 = null; | ||
$namedRange = DefinedName::resolveName($cellAddress1, $sheet, $sheetName); | ||
if ($namedRange !== null) { | ||
$workSheet = $namedRange->getWorkSheet(); | ||
$sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle(); | ||
$value = preg_replace('/^=/', '', $namedRange->getValue()); | ||
self::adjustSheetTitle($sheetTitle, $value); | ||
$cellAddress1 = $sheetTitle . $value; | ||
$cellAddress = $cellAddress1; | ||
$a1 = self::CELLADDRESS_USE_A1; | ||
} | ||
if (strpos($cellAddress, ':') !== false) { | ||
[$cellAddress1, $cellAddress2] = explode(':', $cellAddress); | ||
} | ||
$cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1); | ||
|
||
return [$cellAddress1, $cellAddress2, $cellAddress]; | ||
} | ||
|
||
public static function extractWorksheet(string $cellAddress, Cell $pCell): array | ||
{ | ||
$sheetName = ''; | ||
if (strpos($cellAddress, '!') !== false) { | ||
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true); | ||
$sheetName = trim($sheetName, "'"); | ||
} | ||
|
||
$pSheet = ($sheetName !== '') | ||
? $pCell->getWorksheet()->getParent()->getSheetByName($sheetName) | ||
: $pCell->getWorksheet(); | ||
|
||
return [$cellAddress, $pSheet, $sheetName]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.