Skip to content

Commit 0ee4d96

Browse files
author
Mark Baker
authored
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
1 parent 35b65be commit 0ee4d96

File tree

16 files changed

+244
-100
lines changed

16 files changed

+244
-100
lines changed

src/PhpSpreadsheet/Calculation/Functions.php

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,6 @@ public static function DUMMY()
131131
return '#Not Yet Implemented';
132132
}
133133

134-
/**
135-
* NULL.
136-
*
137-
* Returns the error value #NULL!
138-
*
139-
* @Deprecated 1.23.0
140-
*
141-
* @return string #NULL!
142-
*
143-
*@see Information\ExcelError::null()
144-
* Use the null() method in the Information\Error class instead
145-
*/
146-
public static function null()
147-
{
148-
return Information\ExcelError::null();
149-
}
150-
151134
public static function isMatrixValue($idx)
152135
{
153136
return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
@@ -215,6 +198,23 @@ private static function operandSpecialHandling($operand)
215198
return $operand;
216199
}
217200

201+
/**
202+
* NULL.
203+
*
204+
* Returns the error value #NULL!
205+
*
206+
* @Deprecated 1.23.0
207+
*
208+
* @return string #NULL!
209+
*
210+
*@see Information\ExcelError::null()
211+
* Use the null() method in the Information\Error class instead
212+
*/
213+
public static function null()
214+
{
215+
return Information\ExcelError::null();
216+
}
217+
218218
/**
219219
* NaN.
220220
*
@@ -326,7 +326,7 @@ public static function DIV0()
326326
*
327327
* @Deprecated 1.23.0
328328
*
329-
* @return int|string
329+
* @return array|int|string
330330
*
331331
* @see Information\ExcelError::type()
332332
* Use the type() method in the Information\Error class instead
@@ -668,7 +668,7 @@ public static function flattenSingleValue($value = '')
668668
* @param mixed $cellReference The cell to check
669669
* @param ?Cell $cell The current cell (containing this formula)
670670
*
671-
* @return bool|string
671+
* @return array|bool|string
672672
*/
673673
public static function isFormula($cellReference = '', ?Cell $cell = null)
674674
{
@@ -698,4 +698,13 @@ public static function trimTrailingRange(string $coordinate): string
698698
{
699699
return Worksheet::pregReplace('/:[\\w\$]+$/', '', $coordinate);
700700
}
701+
702+
public static function trimSheetFromCellReference(string $coordinate): string
703+
{
704+
while (strpos($coordinate, '!') !== false) {
705+
$coordinate = substr($coordinate, strpos($coordinate, '!') + 1);
706+
}
707+
708+
return $coordinate;
709+
}
701710
}

src/PhpSpreadsheet/Calculation/Information/ExcelError.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
44

5-
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
5+
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
66

77
class ExcelError
88
{
9+
use ArrayEnabled;
10+
911
/**
1012
* List of error codes.
1113
*
@@ -27,11 +29,13 @@ class ExcelError
2729
*
2830
* @param mixed $value Value to check
2931
*
30-
* @return int|string
32+
* @return array|int|string
3133
*/
3234
public static function type($value = '')
3335
{
34-
$value = Functions::flattenSingleValue($value);
36+
if (is_array($value)) {
37+
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
38+
}
3539

3640
$i = 1;
3741
foreach (self::$errorCodes as $errorCode) {

src/PhpSpreadsheet/Calculation/Information/Value.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
77
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
88
use PhpOffice\PhpSpreadsheet\Cell\Cell;
9+
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
910

1011
class Value
1112
{
@@ -170,26 +171,36 @@ public static function isNonText($value = null)
170171
* @param mixed $cellReference The cell to check
171172
* @param ?Cell $cell The current cell (containing this formula)
172173
*
173-
* @return bool|string
174+
* @return array|bool|string
174175
*/
175176
public static function isFormula($cellReference = '', ?Cell $cell = null)
176177
{
177178
if ($cell === null) {
178179
return ExcelError::REF();
179180
}
180-
$cellReference = Functions::expandDefinedName((string) $cellReference, $cell);
181-
$cellReference = Functions::trimTrailingRange($cellReference);
182181

183-
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
182+
$fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
183+
184+
if (strpos($cellReference, '!') !== false) {
185+
$cellReference = Functions::trimSheetFromCellReference($cellReference);
186+
$cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
187+
if (count($cellReferences) > 1) {
188+
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
189+
}
190+
}
191+
192+
$fullCellReference = Functions::trimTrailingRange($fullCellReference);
193+
194+
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
184195

185-
$cellReference = $matches[6] . $matches[7];
196+
$fullCellReference = $matches[6] . $matches[7];
186197
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
187198

188199
$worksheet = (!empty($worksheetName))
189200
? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
190201
: $cell->getWorksheet();
191202

192-
return ($worksheet !== null) ? $worksheet->getCell($cellReference)->isFormula() : ExcelError::REF();
203+
return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
193204
}
194205

195206
/**

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public function setValueExplicit($value, $dataType)
280280
*/
281281
public function getCalculatedValue($resetLog = true)
282282
{
283-
if ($this->dataType == DataType::TYPE_FORMULA) {
283+
if ($this->dataType === DataType::TYPE_FORMULA) {
284284
try {
285285
$index = $this->getWorksheet()->getParent()->getActiveSheetIndex();
286286
$selected = $this->getWorksheet()->getSelectedCells();
@@ -379,20 +379,16 @@ public function setDataType($dataType)
379379

380380
/**
381381
* Identify if the cell contains a formula.
382-
*
383-
* @return bool
384382
*/
385-
public function isFormula()
383+
public function isFormula(): bool
386384
{
387-
return $this->dataType == DataType::TYPE_FORMULA;
385+
return $this->dataType === DataType::TYPE_FORMULA && $this->getStyle()->getQuotePrefix() === false;
388386
}
389387

390388
/**
391389
* Does this cell contain Data validation rules?
392-
*
393-
* @return bool
394390
*/
395-
public function hasDataValidation()
391+
public function hasDataValidation(): bool
396392
{
397393
if (!isset($this->parent)) {
398394
throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class Div0Test extends TestCase
9+
{
10+
public function testDIV0(): void
11+
{
12+
$result = ExcelError::DIV0();
13+
self::assertEquals('#DIV/0!', $result);
14+
}
15+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6+
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
7+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class ErrorTypeTest extends TestCase
11+
{
12+
public function testErrorTypeNoArgument(): void
13+
{
14+
$result = Functions::errorType();
15+
self::assertSame(ExcelError::NA(), $result);
16+
}
17+
18+
/**
19+
* @dataProvider providerErrorType
20+
*
21+
* @param int|string $expectedResult
22+
* @param mixed $value
23+
*/
24+
public function testErrorType($expectedResult, $value): void
25+
{
26+
$result = Functions::errorType($value);
27+
self::assertSame($expectedResult, $result);
28+
}
29+
30+
public function providerErrorType(): array
31+
{
32+
return require 'tests/data/Calculation/Information/ERROR_TYPE.php';
33+
}
34+
35+
/**
36+
* @dataProvider providerErrorTypeArray
37+
*/
38+
public function testErrorTypeArray(array $expectedResult, string $values): void
39+
{
40+
$calculation = Calculation::getInstance();
41+
42+
$formula = "=ERROR.TYPE({$values})";
43+
$result = $calculation->_calculateFormulaValue($formula);
44+
self::assertEquals($expectedResult, $result);
45+
}
46+
47+
public function providerErrorTypeArray(): array
48+
{
49+
return [
50+
'vector' => [
51+
[[2, 4, 7, ExcelError::NA(), ExcelError::NA(), ExcelError::NA(), 5]],
52+
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP", "#NAME?"}',
53+
],
54+
];
55+
}
56+
}

tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsErrorTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public function providerIsErrorArray(): array
4646
{
4747
return [
4848
'vector' => [
49-
[[true, true, true, false, false, false]],
50-
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP"}',
49+
[[true, true, true, false, false, false, false]],
50+
'{5/0, "#REF!", "#N/A", 1.2, TRUE, "PHP", null}',
5151
],
5252
];
5353
}

tests/PhpSpreadsheetTests/Calculation/IsFormulaTest.php renamed to tests/PhpSpreadsheetTests/Calculation/Functions/Information/IsFormulaTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<?php
22

3-
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
44

5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6+
use PhpOffice\PhpSpreadsheet\Cell\DataType;
57
use PhpOffice\PhpSpreadsheet\NamedRange as NamedRange;
68
use PhpOffice\PhpSpreadsheet\Spreadsheet;
79
use PHPUnit\Framework\TestCase;
@@ -87,4 +89,26 @@ public function testIsFormula(): void
8789

8890
$spreadsheet->disconnectWorksheets();
8991
}
92+
93+
public function testIsFormulaArray(): void
94+
{
95+
$spreadsheet = new Spreadsheet();
96+
$sheet = $spreadsheet->getActiveSheet();
97+
$sheet->getCell('A1')->setValue('=5/2');
98+
$sheet->getCell('A2')->setValueExplicit('=5/2', DataType::TYPE_STRING);
99+
$sheet->getCell('A3')->setValue('=5/0');
100+
$sheet->getCell('A4')->setValue(2.5);
101+
$sheet->getCell('A5')->setValue('=NA()');
102+
$sheet->getCell('A6')->setValue(true);
103+
$sheet->getCell('A7')->setValue('=5/0');
104+
$sheet->getCell('A7')->getStyle()->setQuotePrefix(true);
105+
106+
$calculation = Calculation::getInstance($spreadsheet);
107+
108+
$formula = '=ISFORMULA(A1:A7)';
109+
$result = $calculation->_calculateFormulaValue($formula, 'C1', $sheet->getCell('C1'));
110+
self::assertEquals([true, false, true, false, true, false, false], $result);
111+
112+
$spreadsheet->disconnectWorksheets();
113+
}
90114
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class NaTest extends TestCase
9+
{
10+
public function testNA(): void
11+
{
12+
$result = ExcelError::NA();
13+
self::assertEquals('#N/A', $result);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class NameTest extends TestCase
9+
{
10+
public function testNAME(): void
11+
{
12+
$result = ExcelError::NAME();
13+
self::assertEquals('#NAME?', $result);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class NanTest extends TestCase
9+
{
10+
public function testNAN(): void
11+
{
12+
$result = ExcelError::NAN();
13+
self::assertEquals('#NUM!', $result);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class NullTest extends TestCase
9+
{
10+
public function testNULL(): void
11+
{
12+
$result = Functions::null();
13+
self::assertEquals('#NULL!', $result);
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Information;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class RefTest extends TestCase
9+
{
10+
public function testREF(): void
11+
{
12+
$result = ExcelError::REF();
13+
self::assertEquals('#REF!', $result);
14+
}
15+
}

0 commit comments

Comments
 (0)