Start refactoring the Lookup and Reference functions (#1912)

* Start refactoring the Lookup and Reference functions
 - COLUMN(), COLUMNS(), ROW() and ROWS()
 - LOOKUP(), VLOOKUP() and HLOOKUP()
 - Refactor TRANSPOSE() and ADDRESS() functions into their own classes

* Additional unit tests
 - LOOKUP()
 - TRANSPOSE()
 - ADDRESS()
This commit is contained in:
Mark Baker 2021-03-10 21:18:33 +01:00 committed by GitHub
parent f81ffd9a4f
commit 70f372d88c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1041 additions and 375 deletions

View File

@ -263,7 +263,7 @@ class Calculation
],
'ADDRESS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'cellAddress'],
'functionCall' => [LookupRef\Address::class, 'cell'],
'argumentCount' => '2-5',
],
'AGGREGATE' => [
@ -543,13 +543,14 @@ class Calculation
],
'COLUMN' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'COLUMN'],
'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMN'],
'argumentCount' => '-1',
'passCellReference' => true,
'passByReference' => [true],
],
'COLUMNS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'COLUMNS'],
'functionCall' => [LookupRef\RowColumnInformation::class, 'COLUMNS'],
'argumentCount' => '1',
],
'COMBIN' => [
@ -1231,7 +1232,7 @@ class Calculation
],
'HLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'HLOOKUP'],
'functionCall' => [LookupRef\HLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
'HOUR' => [
@ -1605,7 +1606,7 @@ class Calculation
],
'LOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'LOOKUP'],
'functionCall' => [LookupRef\Lookup::class, 'lookup'],
'argumentCount' => '2,3',
],
'LOWER' => [
@ -2127,13 +2128,14 @@ class Calculation
],
'ROW' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'ROW'],
'functionCall' => [LookupRef\RowColumnInformation::class, 'ROW'],
'argumentCount' => '-1',
'passCellReference' => true,
'passByReference' => [true],
],
'ROWS' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'ROWS'],
'functionCall' => [LookupRef\RowColumnInformation::class, 'ROWS'],
'argumentCount' => '1',
],
'RRI' => [
@ -2449,7 +2451,7 @@ class Calculation
],
'TRANSPOSE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'TRANSPOSE'],
'functionCall' => [LookupRef\Matrix::class, 'transpose'],
'argumentCount' => '1',
],
'TREND' => [
@ -2559,7 +2561,7 @@ class Calculation
],
'VLOOKUP' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [LookupRef::class, 'VLOOKUP'],
'functionCall' => [LookupRef\VLookup::class, 'lookup'],
'argumentCount' => '3,4',
],
'WEBSERVICE' => [

View File

@ -2,6 +2,12 @@
namespace PhpOffice\PhpSpreadsheet\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Address;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Lookup;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\RowColumnInformation;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
@ -17,15 +23,19 @@ class LookupRef
* Excel Function:
* =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
*
* @Deprecated 1.18.0
*
* @see Use the cell() method in the LookupRef\Address class instead
*
* @param mixed $row Row number to use in the cell reference
* @param mixed $column Column number to use in the cell reference
* @param int $relativity Flag indicating the type of reference to return
* 1 or omitted Absolute
* 2 Absolute row; relative column
* 3 Relative row; absolute column
* 4 Relative
* 2 Absolute row; relative column
* 3 Relative row; absolute column
* 4 Relative
* @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style.
* TRUE or omitted CELL_ADDRESS returns an A1-style reference
* TRUE or omitted CELL_ADDRESS returns an A1-style reference
* FALSE CELL_ADDRESS returns an R1C1-style reference
* @param string $sheetText Optional Name of worksheet to use
*
@ -33,87 +43,33 @@ class LookupRef
*/
public static function cellAddress($row, $column, $relativity = 1, $referenceStyle = true, $sheetText = '')
{
$row = Functions::flattenSingleValue($row);
$column = Functions::flattenSingleValue($column);
$relativity = Functions::flattenSingleValue($relativity);
$sheetText = Functions::flattenSingleValue($sheetText);
if (($row < 1) || ($column < 1)) {
return Functions::VALUE();
}
if ($sheetText > '') {
if (strpos($sheetText, ' ') !== false) {
$sheetText = "'" . $sheetText . "'";
}
$sheetText .= '!';
}
if ((!is_bool($referenceStyle)) || $referenceStyle) {
$rowRelative = $columnRelative = '$';
$column = Coordinate::stringFromColumnIndex($column);
if (($relativity == 2) || ($relativity == 4)) {
$columnRelative = '';
}
if (($relativity == 3) || ($relativity == 4)) {
$rowRelative = '';
}
return $sheetText . $columnRelative . $column . $rowRelative . $row;
}
if (($relativity == 2) || ($relativity == 4)) {
$column = '[' . $column . ']';
}
if (($relativity == 3) || ($relativity == 4)) {
$row = '[' . $row . ']';
}
return $sheetText . 'R' . $row . 'C' . $column;
return Address::cell($row, $column, $relativity, $referenceStyle, $sheetText);
}
/**
* COLUMN.
*
* Returns the column number of the given cell reference
* If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
* If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
* reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
* If the cell reference is a range of cells, COLUMN returns the column numbers of each column
* in the reference as a horizontal array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the COLUMN function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
* @Deprecated 1.18.0
*
* @see Use the COLUMN() method in the LookupRef\RowColumnInformation class instead
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
* @return int|int[]
*/
public static function COLUMN($cellAddress = null)
public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
if ($cellAddress === null || trim($cellAddress) === '') {
return 0;
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $value) {
$columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
return (int) Coordinate::columnIndexFromString($columnKey);
}
} else {
[$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
$returnValue = [];
do {
$returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
} while ($startAddress++ != $endAddress);
return $returnValue;
}
$cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
return (int) Coordinate::columnIndexFromString($cellAddress);
}
return RowColumnInformation::COLUMN($cellAddress, $cell);
}
/**
@ -124,73 +80,44 @@ class LookupRef
* Excel Function:
* =COLUMNS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
* @Deprecated 1.18.0
*
* @see Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of columns
*
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
if ($cellAddress === null || $cellAddress === '') {
return 1;
} elseif (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $rows;
}
return $columns;
return RowColumnInformation::COLUMNS($cellAddress);
}
/**
* ROW.
*
* Returns the row number of the given cell reference
* If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
* If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
* reference of the cell in which the ROW function appears; otherwise this function returns 0.
* If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
* as a vertical array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the ROW function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =ROW([cellAddress])
*
* @Deprecated 1.18.0
*
* @see Use the ROW() method in the LookupRef\RowColumnInformation class instead
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
* @return int|mixed[]|string
*/
public static function ROW($cellAddress = null)
public static function ROW($cellAddress = null, ?Cell $cell = null)
{
if ($cellAddress === null || trim($cellAddress) === '') {
return 0;
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $rowValue) {
foreach ($rowValue as $rowKey => $cellValue) {
return (int) preg_replace('/\D/', '', $rowKey);
}
}
} else {
[$sheet, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/\D/', '', $startAddress);
$endAddress = preg_replace('/\D/', '', $endAddress);
$returnValue = [];
do {
$returnValue[][] = (int) $startAddress;
} while ($startAddress++ != $endAddress);
return $returnValue;
}
[$cellAddress] = explode(':', $cellAddress);
return (int) preg_replace('/\D/', '', $cellAddress);
}
return RowColumnInformation::ROW($cellAddress, $cell);
}
/**
@ -201,27 +128,18 @@ class LookupRef
* Excel Function:
* =ROWS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
* @Deprecated 1.18.0
*
* @see Use the ROWS() method in the LookupRef\RowColumnInformation class instead
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of rows
*
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
if ($cellAddress === null || $cellAddress === '') {
return 1;
} elseif (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $columns;
}
return $rows;
return RowColumnInformation::ROWS($cellAddress);
}
/**
@ -669,204 +587,74 @@ class LookupRef
/**
* TRANSPOSE.
*
* @Deprecated 1.18.0
*
* @see Use the transpose() method in the LookupRef\Matrix class instead
*
* @param array $matrixData A matrix of values
*
* @return array
*
* Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix
* Unlike the Excel TRANSPOSE function, which will only work on a single row or column,
* this function will transpose a full matrix
*/
public static function TRANSPOSE($matrixData)
{
$returnMatrix = [];
if (!is_array($matrixData)) {
$matrixData = [[$matrixData]];
}
$column = 0;
foreach ($matrixData as $matrixRow) {
$row = 0;
foreach ($matrixRow as $matrixCell) {
$returnMatrix[$row][$column] = $matrixCell;
++$row;
}
++$column;
}
return $returnMatrix;
}
private static function vlookupSort($a, $b)
{
reset($a);
$firstColumn = key($a);
$aLower = StringHelper::strToLower($a[$firstColumn]);
$bLower = StringHelper::strToLower($b[$firstColumn]);
if ($aLower == $bLower) {
return 0;
}
return ($aLower < $bLower) ? -1 : 1;
return Matrix::transpose($matrixData);
}
/**
* VLOOKUP
* The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
* The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
* in the same row based on the index_number.
*
* @Deprecated 1.18.0
*
* @see Use the lookup() method in the LookupRef\VLookup class instead
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
* @param mixed $index_number The column number in table_array from which the matching value must be returned. The first column is 1.
* @param mixed $index_number The column number in table_array from which the matching value must be returned.
* The first column is 1.
* @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
{
$lookup_value = Functions::flattenSingleValue($lookup_value);
$index_number = Functions::flattenSingleValue($index_number);
$not_exact_match = Functions::flattenSingleValue($not_exact_match);
// index_number must be greater than or equal to 1
if ($index_number < 1) {
return Functions::VALUE();
}
// index_number must be less than or equal to the number of columns in lookup_array
if ((!is_array($lookup_array)) || (empty($lookup_array))) {
return Functions::REF();
}
$f = array_keys($lookup_array);
$firstRow = array_pop($f);
if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
return Functions::REF();
}
$columnKeys = array_keys($lookup_array[$firstRow]);
$returnColumn = $columnKeys[--$index_number];
$firstColumn = array_shift($columnKeys);
if (!$not_exact_match) {
uasort($lookup_array, ['self', 'vlookupSort']);
}
$lookupLower = StringHelper::strToLower($lookup_value);
$rowNumber = $rowValue = false;
foreach ($lookup_array as $rowKey => $rowData) {
$firstLower = StringHelper::strToLower($rowData[$firstColumn]);
// break if we have passed possible keys
if (
(is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
(!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))
) {
break;
}
// remember the last key, but only if datatypes match
if (
(is_numeric($lookup_value) && is_numeric($rowData[$firstColumn])) ||
(!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))
) {
if ($not_exact_match) {
$rowNumber = $rowKey;
continue;
} elseif (
($firstLower == $lookupLower)
// Spreadsheets software returns first exact match,
// we have sorted and we might have broken key orders
// we want the first one (by its initial index)
&& (($rowNumber == false) || ($rowKey < $rowNumber))
) {
$rowNumber = $rowKey;
}
}
}
if ($rowNumber !== false) {
// return the appropriate value
return $lookup_array[$rowNumber][$returnColumn];
}
return Functions::NA();
return VLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* HLOOKUP
* The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value in the same column based on the index_number.
* The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
* in the same column based on the index_number.
*
* @Deprecated 1.18.0
*
* @see Use the lookup() method in the LookupRef\HLookup class instead
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_array The range of cells being searched
* @param mixed $index_number The row number in table_array from which the matching value must be returned. The first row is 1.
* @param mixed $index_number The row number in table_array from which the matching value must be returned.
* The first row is 1.
* @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
{
$lookup_value = Functions::flattenSingleValue($lookup_value);
$index_number = Functions::flattenSingleValue($index_number);
$not_exact_match = Functions::flattenSingleValue($not_exact_match);
// index_number must be greater than or equal to 1
if ($index_number < 1) {
return Functions::VALUE();
}
// index_number must be less than or equal to the number of columns in lookup_array
if ((!is_array($lookup_array)) || (empty($lookup_array))) {
return Functions::REF();
}
$f = array_keys($lookup_array);
$firstRow = reset($f);
if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array))) {
return Functions::REF();
}
$firstkey = $f[0] - 1;
$returnColumn = $firstkey + $index_number;
$firstColumn = array_shift($f);
$rowNumber = null;
foreach ($lookup_array[$firstColumn] as $rowKey => $rowData) {
// break if we have passed possible keys
$bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
$bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
$lookupLower = StringHelper::strToLower($lookup_value);
$rowDataLower = StringHelper::strToLower($rowData);
if (
$not_exact_match && (
($bothNumeric && $rowData > $lookup_value) ||
($bothNotNumeric && $rowDataLower > $lookupLower)
)
) {
break;
}
// Remember the last key, but only if datatypes match (as in VLOOKUP)
if ($bothNumeric || $bothNotNumeric) {
if ($not_exact_match) {
$rowNumber = $rowKey;
continue;
} elseif (
$rowDataLower === $lookupLower
&& ($rowNumber === null || $rowKey < $rowNumber)
) {
$rowNumber = $rowKey;
}
}
}
if ($rowNumber !== null) {
// otherwise return the appropriate value
return $lookup_array[$returnColumn][$rowNumber];
}
return Functions::NA();
return HLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
}
/**
* LOOKUP
* The LOOKUP function searches for value either from a one-row or one-column range or from an array.
*
* @Deprecated 1.18.0
*
* @see Use the lookup() method in the LookupRef\Lookup class instead
*
* @param mixed $lookup_value The value that you want to match in lookup_array
* @param mixed $lookup_vector The range of cells being searched
* @param null|mixed $result_vector The column from which the matching value must be returned
@ -875,66 +663,7 @@ class LookupRef
*/
public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
{
$lookup_value = Functions::flattenSingleValue($lookup_value);
if (!is_array($lookup_vector)) {
return Functions::NA();
}
$hasResultVector = isset($result_vector);
$lookupRows = count($lookup_vector);
$l = array_keys($lookup_vector);
$l = array_shift($l);
$lookupColumns = count($lookup_vector[$l]);
// we correctly orient our results
if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
$lookup_vector = self::TRANSPOSE($lookup_vector);
$lookupRows = count($lookup_vector);
$l = array_keys($lookup_vector);
$lookupColumns = count($lookup_vector[array_shift($l)]);
}
if ($result_vector === null) {
$result_vector = $lookup_vector;
}
$resultRows = count($result_vector);
$l = array_keys($result_vector);
$l = array_shift($l);
$resultColumns = count($result_vector[$l]);
// we correctly orient our results
if ($resultRows === 1 && $resultColumns > 1) {
$result_vector = self::TRANSPOSE($result_vector);
$resultRows = count($result_vector);
$r = array_keys($result_vector);
$resultColumns = count($result_vector[array_shift($r)]);
}
if ($lookupRows === 2 && !$hasResultVector) {
$result_vector = array_pop($lookup_vector);
$lookup_vector = array_shift($lookup_vector);
}
if ($lookupColumns !== 2) {
foreach ($lookup_vector as &$value) {
if (is_array($value)) {
$k = array_keys($value);
$key1 = $key2 = array_shift($k);
++$key2;
$dataValue1 = $value[$key1];
} else {
$key1 = 0;
$key2 = 1;
$dataValue1 = $value;
}
$dataValue2 = array_shift($result_vector);
if (is_array($dataValue2)) {
$dataValue2 = array_shift($dataValue2);
}
$value = [$key1 => $dataValue1, $key2 => $dataValue2];
}
unset($value);
}
return self::VLOOKUP($lookup_value, $lookup_vector, 2);
return Lookup::lookup($lookup_value, $lookup_vector, $result_vector);
}
/**

View File

@ -0,0 +1,97 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
class Address
{
public const ADDRESS_ABSOLUTE = 1;
public const ADDRESS_COLUMN_RELATIVE = 2;
public const ADDRESS_ROW_RELATIVE = 3;
public const ADDRESS_RELATIVE = 4;
public const REFERENCE_STYLE_A1 = true;
public const REFERENCE_STYLE_R1C1 = false;
/**
* ADDRESS.
*
* Creates a cell address as text, given specified row and column numbers.
*
* Excel Function:
* =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
*
* @param mixed $row Row number to use in the cell reference
* @param mixed $column Column number to use in the cell reference
* @param int $relativity Flag indicating the type of reference to return
* 1 or omitted Absolute
* 2 Absolute row; relative column
* 3 Relative row; absolute column
* 4 Relative
* @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style.
* TRUE or omitted ADDRESS returns an A1-style reference
* FALSE ADDRESS returns an R1C1-style reference
* @param string $sheetName Optional Name of worksheet to use
*
* @return string
*/
public static function cell($row, $column, $relativity = 1, $referenceStyle = true, $sheetName = '')
{
$row = Functions::flattenSingleValue($row);
$column = Functions::flattenSingleValue($column);
$relativity = Functions::flattenSingleValue($relativity);
$sheetName = Functions::flattenSingleValue($sheetName);
if (($row < 1) || ($column < 1)) {
return Functions::VALUE();
}
$sheetName = self::sheetName($sheetName);
if ((!is_bool($referenceStyle)) || $referenceStyle === self::REFERENCE_STYLE_A1) {
return self::formatAsA1($row, $column, $relativity, $sheetName);
}
return self::formatAsR1C1($row, $column, $relativity, $sheetName);
}
private static function sheetName(string $sheetName)
{
if ($sheetName > '') {
if (strpos($sheetName, ' ') !== false || strpos($sheetName, '[') !== false) {
$sheetName = "'{$sheetName}'";
}
$sheetName .= '!';
}
return $sheetName;
}
private static function formatAsA1(int $row, int $column, int $relativity, string $sheetName): string
{
$rowRelative = $columnRelative = '$';
if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$columnRelative = '';
}
if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$rowRelative = '';
}
$column = Coordinate::stringFromColumnIndex($column);
return "{$sheetName}{$columnRelative}{$column}{$rowRelative}{$row}";
}
private static function formatAsR1C1(int $row, int $column, int $relativity, string $sheetName): string
{
if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$column = "[{$column}]";
}
if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$row = "[{$row}]";
}
return "{$sheetName}R{$row}C{$column}";
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class HLookup extends LookupBase
{
/**
* HLOOKUP
* The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
* in the same column based on the index_number.
*
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
* @param mixed $indexNumber The row number in table_array from which the matching value must be returned.
* The first row is 1.
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
{
$lookupValue = Functions::flattenSingleValue($lookupValue);
$indexNumber = Functions::flattenSingleValue($indexNumber);
$notExactMatch = Functions::flattenSingleValue($notExactMatch);
try {
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();
}
$f = array_keys($lookupArray);
$firstRow = reset($f);
if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
return Functions::REF();
}
$firstkey = $f[0] - 1;
$returnColumn = $firstkey + $indexNumber;
$firstColumn = array_shift($f);
$rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
if ($rowNumber !== null) {
// otherwise return the appropriate value
return $lookupArray[$returnColumn][$rowNumber];
}
return Functions::NA();
}
private static function hLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
{
$lookupLower = StringHelper::strToLower($lookupValue);
$rowNumber = null;
foreach ($lookupArray[$column] as $rowKey => $rowData) {
// break if we have passed possible keys
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
$cellDataLower = StringHelper::strToLower($rowData);
if (
$notExactMatch &&
(($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
) {
break;
}
$rowNumber = self::checkMatch(
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
$rowKey,
$cellDataLower,
$lookupLower,
$rowNumber
);
}
return $rowNumber;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
class Lookup
{
/**
* LOOKUP
* The LOOKUP function searches for value either from a one-row or one-column range or from an array.
*
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupVector The range of cells being searched
* @param null|mixed $resultVector The column from which the matching value must be returned
*
* @return mixed The value of the found cell
*/
public static function lookup($lookupValue, $lookupVector, $resultVector = null)
{
$lookupValue = Functions::flattenSingleValue($lookupValue);
if (!is_array($lookupVector)) {
return Functions::NA();
}
$hasResultVector = isset($resultVector);
$lookupRows = self::rowCount($lookupVector);
$lookupColumns = self::columnCount($lookupVector);
// we correctly orient our results
if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
$lookupVector = LookupRef::TRANSPOSE($lookupVector);
$lookupRows = self::rowCount($lookupVector);
$lookupColumns = self::columnCount($lookupVector);
}
$resultVector = self::verifyResultVector($lookupVector, $resultVector);
if ($lookupRows === 2 && !$hasResultVector) {
$resultVector = array_pop($lookupVector);
$lookupVector = array_shift($lookupVector);
}
if ($lookupColumns !== 2) {
$lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
}
return VLookup::lookup($lookupValue, $lookupVector, 2);
}
private static function verifyLookupValues(array $lookupVector, array $resultVector): array
{
foreach ($lookupVector as &$value) {
if (is_array($value)) {
$k = array_keys($value);
$key1 = $key2 = array_shift($k);
++$key2;
$dataValue1 = $value[$key1];
} else {
$key1 = 0;
$key2 = 1;
$dataValue1 = $value;
}
$dataValue2 = array_shift($resultVector);
if (is_array($dataValue2)) {
$dataValue2 = array_shift($dataValue2);
}
$value = [$key1 => $dataValue1, $key2 => $dataValue2];
}
unset($value);
return $lookupVector;
}
private static function verifyResultVector(array $lookupVector, $resultVector)
{
if ($resultVector === null) {
$resultVector = $lookupVector;
}
$resultRows = self::rowCount($resultVector);
$resultColumns = self::columnCount($resultVector);
// we correctly orient our results
if ($resultRows === 1 && $resultColumns > 1) {
$resultVector = LookupRef::TRANSPOSE($resultVector);
}
return $resultVector;
}
private static function rowCount(array $dataArray): int
{
return count($dataArray);
}
private static function columnCount(array $dataArray): int
{
$rowKeys = array_keys($dataArray);
$row = array_shift($rowKeys);
return count($dataArray[$row]);
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
abstract class LookupBase
{
protected static function validateIndexLookup($lookup_array, $index_number)
{
// index_number must be a number greater than or equal to 1
if (!is_numeric($index_number) || $index_number < 1) {
throw new Exception(Functions::VALUE());
}
// index_number must be less than or equal to the number of columns in lookup_array
if ((!is_array($lookup_array)) || (empty($lookup_array))) {
throw new Exception(Functions::REF());
}
return (int) $index_number;
}
protected static function checkMatch(
bool $bothNumeric,
bool $bothNotNumeric,
$notExactMatch,
int $rowKey,
string $cellDataLower,
string $lookupLower,
?int $rowNumber
): ?int {
// remember the last key, but only if datatypes match
if ($bothNumeric || $bothNotNumeric) {
// Spreadsheets software returns first exact match,
// we have sorted and we might have broken key orders
// we want the first one (by its initial index)
if ($notExactMatch) {
$rowNumber = $rowKey;
} elseif (($cellDataLower == $lookupLower) && (($rowNumber === null) || ($rowKey < $rowNumber))) {
$rowNumber = $rowKey;
}
}
return $rowNumber;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
class Matrix
{
/**
* TRANSPOSE.
*
* @param array $matrixData A matrix of values
*
* @return array
*/
public static function transpose($matrixData)
{
$returnMatrix = [];
if (!is_array($matrixData)) {
$matrixData = [[$matrixData]];
}
$column = 0;
foreach ($matrixData as $matrixRow) {
$row = 0;
foreach ($matrixRow as $matrixCell) {
$returnMatrix[$row][$column] = $matrixCell;
++$row;
}
++$column;
}
return $returnMatrix;
}
}

View File

@ -0,0 +1,172 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class RowColumnInformation
{
/**
* COLUMN.
*
* Returns the column number of the given cell reference
* If the cell reference is a range of cells, COLUMN returns the column numbers of each column
* in the reference as a horizontal array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the COLUMN function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
* @return int|int[]
*/
public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
if ($cellAddress === null || (!is_array($cellAddress) && trim($cellAddress) === '')) {
return ($cell !== null) ? (int) Coordinate::columnIndexFromString($cell->getColumn()) : 1;
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $value) {
$columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
return (int) Coordinate::columnIndexFromString($columnKey);
}
} else {
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
$returnValue = [];
do {
$returnValue[] = (int) Coordinate::columnIndexFromString($startAddress);
} while ($startAddress++ != $endAddress);
return $returnValue;
}
$cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
return (int) Coordinate::columnIndexFromString($cellAddress);
}
}
/**
* COLUMNS.
*
* Returns the number of columns in an array or reference.
*
* Excel Function:
* =COLUMNS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of columns
*
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
if ($cellAddress === null || $cellAddress === '') {
return 1;
} elseif (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $rows;
}
return $columns;
}
/**
* ROW.
*
* Returns the row number of the given cell reference
* If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
* as a vertical array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the ROW function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =ROW([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
* @return int|mixed[]|string
*/
public static function ROW($cellAddress = null, ?Cell $pCell = null)
{
if ($cellAddress === null || (!is_array($cellAddress) && trim($cellAddress) === '')) {
return ($pCell !== null) ? $pCell->getRow() : 1;
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $rowValue) {
foreach ($rowValue as $rowKey => $cellValue) {
return (int) preg_replace('/\D/', '', $rowKey);
}
}
} else {
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/\D/', '', $startAddress);
$endAddress = preg_replace('/\D/', '', $endAddress);
$returnValue = [];
do {
$returnValue[][] = (int) $startAddress;
} while ($startAddress++ != $endAddress);
return $returnValue;
}
[$cellAddress] = explode(':', $cellAddress);
return (int) preg_replace('/\D/', '', $cellAddress);
}
}
/**
* ROWS.
*
* Returns the number of rows in an array or reference.
*
* Excel Function:
* =ROWS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of rows
*
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
if ($cellAddress === null || $cellAddress === '') {
return 1;
} elseif (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $columns;
}
return $rows;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class VLookup extends LookupBase
{
/**
* VLOOKUP
* The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
* in the same row based on the index_number.
*
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
* @param mixed $indexNumber The column number in table_array from which the matching value must be returned.
* The first column is 1.
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
{
$lookupValue = Functions::flattenSingleValue($lookupValue);
$indexNumber = Functions::flattenSingleValue($indexNumber);
$notExactMatch = Functions::flattenSingleValue($notExactMatch);
try {
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();
}
$f = array_keys($lookupArray);
$firstRow = array_pop($f);
if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
return Functions::REF();
}
$columnKeys = array_keys($lookupArray[$firstRow]);
$returnColumn = $columnKeys[--$indexNumber];
$firstColumn = array_shift($columnKeys);
if (!$notExactMatch) {
uasort($lookupArray, ['self', 'vlookupSort']);
}
$rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
if ($rowNumber !== null) {
// return the appropriate value
return $lookupArray[$rowNumber][$returnColumn];
}
return Functions::NA();
}
private static function vlookupSort($a, $b)
{
reset($a);
$firstColumn = key($a);
$aLower = StringHelper::strToLower($a[$firstColumn]);
$bLower = StringHelper::strToLower($b[$firstColumn]);
if ($aLower == $bLower) {
return 0;
}
return ($aLower < $bLower) ? -1 : 1;
}
private static function vLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
{
$lookupLower = StringHelper::strToLower($lookupValue);
$rowNumber = null;
foreach ($lookupArray as $rowKey => $rowData) {
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
$cellDataLower = StringHelper::strToLower($rowData[$column]);
// break if we have passed possible keys
if (
$notExactMatch &&
(($bothNumeric && ($rowData[$column] > $lookupValue)) ||
($bothNotNumeric && ($cellDataLower > $lookupLower)))
) {
break;
}
$rowNumber = self::checkMatch(
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
$rowKey,
$cellDataLower,
$lookupLower,
$rowNumber
);
}
return $rowNumber;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PHPUnit\Framework\TestCase;
class AddressTest extends TestCase
{
protected function setUp(): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
}
/**
* @dataProvider providerADDRESS
*
* @param mixed $expectedResult
*/
public function testADDRESS($expectedResult, ...$args): void
{
$result = LookupRef::cellAddress(...$args);
self::assertEquals($expectedResult, $result);
}
public function providerADDRESS()
{
return require 'tests/data/Calculation/LookupRef/ADDRESS.php';
}
}

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PHPUnit\Framework\TestCase;
class ColumnTest extends TestCase
@ -17,8 +18,9 @@ class ColumnTest extends TestCase
* @dataProvider providerCOLUMN
*
* @param mixed $expectedResult
* @param null|mixed $cellReference
*/
public function testCOLUMN($expectedResult, string $cellReference): void
public function testCOLUMN($expectedResult, $cellReference = null): void
{
$result = LookupRef::COLUMN($cellReference);
self::assertSame($expectedResult, $result);
@ -28,4 +30,17 @@ class ColumnTest extends TestCase
{
return require 'tests/data/Calculation/LookupRef/COLUMN.php';
}
public function testCOLUMNwithNull(): void
{
$cell = $this->getMockBuilder(Cell::class)
->setMethods(['getColumn'])
->disableOriginalConstructor()
->getMock();
$cell->method('getColumn')
->willReturn('D');
$result = LookupRef::COLUMN(null, $cell);
self::assertSame(4, $result);
}
}

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PHPUnit\Framework\TestCase;
class RowTest extends TestCase
@ -17,8 +18,9 @@ class RowTest extends TestCase
* @dataProvider providerROW
*
* @param mixed $expectedResult
* @param null|mixed $cellReference
*/
public function testROW($expectedResult, string $cellReference): void
public function testROW($expectedResult, $cellReference = null): void
{
$result = LookupRef::ROW($cellReference);
self::assertSame($expectedResult, $result);
@ -28,4 +30,17 @@ class RowTest extends TestCase
{
return require 'tests/data/Calculation/LookupRef/ROW.php';
}
public function testROWwithNull(): void
{
$cell = $this->getMockBuilder(Cell::class)
->setMethods(['getRow'])
->disableOriginalConstructor()
->getMock();
$cell->method('getRow')
->willReturn(3);
$result = LookupRef::ROW(null, $cell);
self::assertSame(3, $result);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PHPUnit\Framework\TestCase;
class TransposeTest extends TestCase
{
protected function setUp(): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
}
/**
* @dataProvider providerTRANSPOSE
*
* @param mixed $expectedResult
* @param mixed $matrix
*/
public function testTRANSPOSE($expectedResult, $matrix): void
{
$result = LookupRef::TRANSPOSE($matrix);
self::assertEquals($expectedResult, $result);
}
public function providerTRANSPOSE()
{
return require 'tests/data/Calculation/LookupRef/TRANSPOSE.php';
}
}

View File

@ -65,10 +65,11 @@ class DefaultValueBinderTest extends TestCase
* @dataProvider providerDataTypeForValue
*
* @param mixed $expectedResult
* @param mixed $value
*/
public function testDataTypeForValue($expectedResult, ...$args): void
public function testDataTypeForValue($expectedResult, $value): void
{
$result = DefaultValueBinder::dataTypeForValue(...$args);
$result = DefaultValueBinder::dataTypeForValue($value);
self::assertEquals($expectedResult, $result);
}

View File

@ -0,0 +1,56 @@
<?php
return [
[
'$C$2',
2,
3,
],
[
'C$2',
2,
3,
2,
],
[
'$C2',
2,
3,
3,
],
[
'R2C[3]',
2,
3,
2,
false,
],
[
'R[2]C3',
2,
3,
3,
false,
],
[
"'[Book1]Sheet1'!R2C3",
2,
3,
1,
false,
'[Book1]Sheet1',
],
[
"'EXCEL SHEET'!R2C3",
2,
3,
1,
false,
'EXCEL SHEET',
],
[
'#VALUE!',
-2,
-2,
],
];

View File

@ -1,6 +1,10 @@
<?php
return [
[
1,
null,
],
[
2,
'B13',
@ -9,4 +13,20 @@ return [
[2, 3, 4],
'B2:D2',
],
[
2,
['B' => 'B5', 'C' => 'C5', 'D' => 'D5'],
],
[
[2, 3, 4],
'B2:D3',
],
[
[2, 3, 4],
'Sheet1!B2:D2',
],
[
[2, 3, 4],
'"WorkSheet #1"!B2:D2',
],
];

View File

@ -308,4 +308,24 @@ return [
2,
false,
],
[
'#VALUE!',
'B',
[
['Selection column', 'C', 'B', 'A'],
['Value to retrieve', 3, 2, 1],
],
'Nan',
false,
],
[
'#REF!',
'B',
[
'Selection column',
'Value to retrieve',
],
2,
false,
],
];

View File

@ -1,7 +1,6 @@
<?php
return [
[ // Office reference example #1
'orange',
4.19,
@ -74,7 +73,6 @@ return [
['blue'],
],
],
[ // Array form test
'orange',
4.2,
@ -86,7 +84,6 @@ return [
[6, 39, 'blue'],
],
],
[
5,
'x',
@ -95,20 +92,40 @@ return [
[1, 2, 3, 4, 5],
],
],
[
'author_100',
100,
'author_101',
101,
[
[100],
[101],
[102],
],
[
['author_100'],
['author_101'],
['author_102'],
],
],
[
'author_101',
101,
[
[100],
[101],
[102],
],
[
['author_100', 'author_101', 'author_102'],
],
],
[
101,
101,
[
[100],
[101],
],
],
[
'#N/A',
'10y2',
@ -123,5 +140,14 @@ return [
[10.0],
],
],
[
'#N/A',
'10y2',
'Not an Array',
[
[2.0],
[7.0],
[10.0],
],
],
];

View File

@ -1,6 +1,10 @@
<?php
return [
[
1,
null,
],
[
10,
'C10',
@ -9,4 +13,16 @@ return [
[[10], [11], [12]],
'C10:C12',
],
[
[[10], [11], [12]],
'C10:D12',
],
[
[[10], [11], [12]],
'Sheet1!C10:C12',
],
[
[[10], [11], [12]],
'"WorkSheet #1"!C10:C12',
],
];

View File

@ -0,0 +1,32 @@
<?php
return [
[
[
['Jan', 'Feb', 'Mar', 'Apr'],
[100, 200, 150, 300],
],
[
['Jan', 100],
['Feb', 200],
['Mar', 150],
['Apr', 300],
],
],
[
[
['a', 'aa'],
['b', 'ab'],
['c', 'ac'],
['d', 'ad'],
],
[
['a', 'b', 'c', 'd'],
['aa', 'ab', 'ac', 'ad'],
],
],
[
[[3.14]],
3.14,
],
];

View File

@ -312,7 +312,6 @@ return [
2,
false,
],
[
'#N/A',
'10y2',
@ -323,5 +322,31 @@ return [
],
2.0,
],
[
'#VALUE!',
'10y2',
[
['5y-1', 2.0],
['10y1', 7.0],
['10y2', 10.0],
],
'NaN',
],
[
'#REF!',
'10y2',
[
],
2.0,
],
[
'#REF!',
'10y2',
[
2.0,
7.0,
10.0,
],
2.0,
],
];