Array-enable the ISFORMULA() function (#2610)

Implement Array-enabled for  ERROR.TYPE() function
Extract ERROR.TYPE() function tests into separate test file
Extract error function tests into separate test files

And thus complete the implemented Information functions
This commit is contained in:
Mark Baker 2022-02-20 19:32:13 +01:00 committed by GitHub
parent 35b65bef8c
commit 0ee4d96576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 244 additions and 100 deletions

View File

@ -131,23 +131,6 @@ class Functions
return '#Not Yet Implemented';
}
/**
* NULL.
*
* Returns the error value #NULL!
*
* @Deprecated 1.23.0
*
* @return string #NULL!
*
*@see Information\ExcelError::null()
* Use the null() method in the Information\Error class instead
*/
public static function null()
{
return Information\ExcelError::null();
}
public static function isMatrixValue($idx)
{
return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
@ -215,6 +198,23 @@ class Functions
return $operand;
}
/**
* NULL.
*
* Returns the error value #NULL!
*
* @Deprecated 1.23.0
*
* @return string #NULL!
*
*@see Information\ExcelError::null()
* Use the null() method in the Information\Error class instead
*/
public static function null()
{
return Information\ExcelError::null();
}
/**
* NaN.
*
@ -326,7 +326,7 @@ class Functions
*
* @Deprecated 1.23.0
*
* @return int|string
* @return array|int|string
*
* @see Information\ExcelError::type()
* Use the type() method in the Information\Error class instead
@ -668,7 +668,7 @@ class Functions
* @param mixed $cellReference The cell to check
* @param ?Cell $cell The current cell (containing this formula)
*
* @return bool|string
* @return array|bool|string
*/
public static function isFormula($cellReference = '', ?Cell $cell = null)
{
@ -698,4 +698,13 @@ class Functions
{
return Worksheet::pregReplace('/:[\\w\$]+$/', '', $coordinate);
}
public static function trimSheetFromCellReference(string $coordinate): string
{
while (strpos($coordinate, '!') !== false) {
$coordinate = substr($coordinate, strpos($coordinate, '!') + 1);
}
return $coordinate;
}
}

View File

@ -2,10 +2,12 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
class ExcelError
{
use ArrayEnabled;
/**
* List of error codes.
*
@ -27,11 +29,13 @@ class ExcelError
*
* @param mixed $value Value to check
*
* @return int|string
* @return array|int|string
*/
public static function type($value = '')
{
$value = Functions::flattenSingleValue($value);
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
$i = 1;
foreach (self::$errorCodes as $errorCode) {

View File

@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
class Value
{
@ -170,26 +171,36 @@ class Value
* @param mixed $cellReference The cell to check
* @param ?Cell $cell The current cell (containing this formula)
*
* @return bool|string
* @return array|bool|string
*/
public static function isFormula($cellReference = '', ?Cell $cell = null)
{
if ($cell === null) {
return ExcelError::REF();
}
$cellReference = Functions::expandDefinedName((string) $cellReference, $cell);
$cellReference = Functions::trimTrailingRange($cellReference);
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
$fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
$cellReference = $matches[6] . $matches[7];
if (strpos($cellReference, '!') !== false) {
$cellReference = Functions::trimSheetFromCellReference($cellReference);
$cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
if (count($cellReferences) > 1) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
}
}
$fullCellReference = Functions::trimTrailingRange($fullCellReference);
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
$fullCellReference = $matches[6] . $matches[7];
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
$worksheet = (!empty($worksheetName))
? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
: $cell->getWorksheet();
return ($worksheet !== null) ? $worksheet->getCell($cellReference)->isFormula() : ExcelError::REF();
return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
}
/**

View File

@ -280,7 +280,7 @@ class Cell
*/
public function getCalculatedValue($resetLog = true)
{
if ($this->dataType == DataType::TYPE_FORMULA) {
if ($this->dataType === DataType::TYPE_FORMULA) {
try {
$index = $this->getWorksheet()->getParent()->getActiveSheetIndex();
$selected = $this->getWorksheet()->getSelectedCells();
@ -379,20 +379,16 @@ class Cell
/**
* Identify if the cell contains a formula.
*
* @return bool
*/
public function isFormula()
public function isFormula(): bool
{
return $this->dataType == DataType::TYPE_FORMULA;
return $this->dataType === DataType::TYPE_FORMULA && $this->getStyle()->getQuotePrefix() === false;
}
/**
* Does this cell contain Data validation rules?
*
* @return bool
*/
public function hasDataValidation()
public function hasDataValidation(): bool
{
if (!isset($this->parent)) {
throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class Div0Test extends TestCase
{
public function testDIV0(): void
{
$result = ExcelError::DIV0();
self::assertEquals('#DIV/0!', $result);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class ErrorTypeTest extends TestCase
{
public function testErrorTypeNoArgument(): void
{
$result = Functions::errorType();
self::assertSame(ExcelError::NA(), $result);
}
/**
* @dataProvider providerErrorType
*
* @param int|string $expectedResult
* @param mixed $value
*/
public function testErrorType($expectedResult, $value): void
{
$result = Functions::errorType($value);
self::assertSame($expectedResult, $result);
}
public function providerErrorType(): array
{
return require 'tests/data/Calculation/Information/ERROR_TYPE.php';
}
/**
* @dataProvider providerErrorTypeArray
*/
public function testErrorTypeArray(array $expectedResult, string $values): void
{
$calculation = Calculation::getInstance();
$formula = "=ERROR.TYPE({$values})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEquals($expectedResult, $result);
}
public function providerErrorTypeArray(): array
{
return [
'vector' => [
[[2, 4, 7, ExcelError::NA(), ExcelError::NA(), ExcelError::NA(), 5]],
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP", "#NAME?"}',
],
];
}
}

View File

@ -46,8 +46,8 @@ class IsErrorTest extends TestCase
{
return [
'vector' => [
[[true, true, true, false, false, false]],
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP"}',
[[true, true, true, false, false, false, false]],
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP", null}',
],
];
}

View File

@ -1,7 +1,9 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\NamedRange as NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
@ -87,4 +89,26 @@ class IsFormulaTest extends TestCase
$spreadsheet->disconnectWorksheets();
}
public function testIsFormulaArray(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue('=5/2');
$sheet->getCell('A2')->setValueExplicit('=5/2', DataType::TYPE_STRING);
$sheet->getCell('A3')->setValue('=5/0');
$sheet->getCell('A4')->setValue(2.5);
$sheet->getCell('A5')->setValue('=NA()');
$sheet->getCell('A6')->setValue(true);
$sheet->getCell('A7')->setValue('=5/0');
$sheet->getCell('A7')->getStyle()->setQuotePrefix(true);
$calculation = Calculation::getInstance($spreadsheet);
$formula = '=ISFORMULA(A1:A7)';
$result = $calculation->_calculateFormulaValue($formula, 'C1', $sheet->getCell('C1'));
self::assertEquals([true, false, true, false, true, false, false], $result);
$spreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class NaTest extends TestCase
{
public function testNA(): void
{
$result = ExcelError::NA();
self::assertEquals('#N/A', $result);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class NameTest extends TestCase
{
public function testNAME(): void
{
$result = ExcelError::NAME();
self::assertEquals('#NAME?', $result);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class NanTest extends TestCase
{
public function testNAN(): void
{
$result = ExcelError::NAN();
self::assertEquals('#NUM!', $result);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class NullTest extends TestCase
{
public function testNULL(): void
{
$result = Functions::null();
self::assertEquals('#NULL!', $result);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class RefTest extends TestCase
{
public function testREF(): void
{
$result = ExcelError::REF();
self::assertEquals('#REF!', $result);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PHPUnit\Framework\TestCase;
class ValueTest extends TestCase
{
public function testVALUE(): void
{
$result = ExcelError::VALUE();
self::assertEquals('#VALUE!', $result);
}
}

View File

@ -74,64 +74,6 @@ class FunctionsTest extends TestCase
self::assertEquals('#Not Yet Implemented', $result);
}
public function testDIV0(): void
{
$result = ExcelError::DIV0();
self::assertEquals('#DIV/0!', $result);
}
public function testNA(): void
{
$result = ExcelError::NA();
self::assertEquals('#N/A', $result);
}
public function testNAN(): void
{
$result = ExcelError::NAN();
self::assertEquals('#NUM!', $result);
}
public function testNAME(): void
{
$result = ExcelError::NAME();
self::assertEquals('#NAME?', $result);
}
public function testREF(): void
{
$result = ExcelError::REF();
self::assertEquals('#REF!', $result);
}
public function testNULL(): void
{
$result = Functions::null();
self::assertEquals('#NULL!', $result);
}
public function testVALUE(): void
{
$result = ExcelError::VALUE();
self::assertEquals('#VALUE!', $result);
}
/**
* @dataProvider providerErrorType
*
* @param mixed $expectedResult
*/
public function testErrorType($expectedResult, ...$args): void
{
$result = Functions::errorType(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
public function providerErrorType(): array
{
return require 'tests/data/Calculation/Functions/ERROR_TYPE.php';
}
/**
* @dataProvider providerIfCondition
*

View File

@ -1,9 +1,6 @@
<?php
return [
[
'#N/A',
],
[
'#N/A',
null,