diff --git a/src/PhpSpreadsheet/Calculation/Logical.php b/src/PhpSpreadsheet/Calculation/Logical.php index b267e1f0b5..d5d993ae3a 100644 --- a/src/PhpSpreadsheet/Calculation/Logical.php +++ b/src/PhpSpreadsheet/Calculation/Logical.php @@ -163,7 +163,7 @@ public static function logicalXor(...$args) * * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE * - * @return bool|string the boolean inverse of the argument + * @return array|bool|string the boolean inverse of the argument */ public static function NOT($logical = false) { diff --git a/src/PhpSpreadsheet/Calculation/Logical/Conditional.php b/src/PhpSpreadsheet/Calculation/Logical/Conditional.php index e84d0f33e4..12d5b85533 100644 --- a/src/PhpSpreadsheet/Calculation/Logical/Conditional.php +++ b/src/PhpSpreadsheet/Calculation/Logical/Conditional.php @@ -2,11 +2,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Logical; +use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Conditional { + use ArrayEnabled; + /** * STATEMENT_IF. * @@ -34,7 +37,9 @@ class Conditional * * @param mixed $condition Condition to evaluate * @param mixed $returnIfTrue Value to return when condition is true + * Note that this can be an array value * @param mixed $returnIfFalse Optional value to return when condition is false + * Note that this can be an array value * * @return mixed The value of returnIfTrue or returnIfFalse determined by condition */ @@ -45,8 +50,8 @@ public static function statementIf($condition = true, $returnIfTrue = 0, $return } $condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition); - $returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue); - $returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse); + $returnIfTrue = $returnIfTrue ?? 0; + $returnIfFalse = $returnIfFalse ?? false; return ($condition) ? $returnIfTrue : $returnIfFalse; } @@ -67,9 +72,11 @@ public static function statementIf($condition = true, $returnIfTrue = 0, $return * result1, result2, ... result_n * A list of results. The SWITCH function returns the corresponding result when a value * matches expression. + * Note that these can be array values to be returned * default * Optional. It is the default to return if expression does not match any of the values * (value1, value2, ... value_n). + * Note that this can be an array value to be returned * * @param mixed $arguments Statement arguments * @@ -113,14 +120,21 @@ public static function statementSwitch(...$arguments) * =IFERROR(testValue,errorpart) * * @param mixed $testValue Value to check, is also the value returned when no error + * Or can be an array of values * @param mixed $errorpart Value to return when testValue is an error condition + * Note that this can be an array value to be returned * * @return mixed The value of errorpart or testValue determined by error condition + * If an array of values is passed as the $testValue argument, then the returned result will also be + * an array with the same dimensions */ public static function IFERROR($testValue = '', $errorpart = '') { - $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue); - $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart); + if (is_array($testValue)) { + return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart); + } + + $errorpart = $errorpart ?? ''; return self::statementIf(Functions::isError($testValue), $errorpart, $testValue); } @@ -132,14 +146,21 @@ public static function IFERROR($testValue = '', $errorpart = '') * =IFNA(testValue,napart) * * @param mixed $testValue Value to check, is also the value returned when not an NA + * Or can be an array of values * @param mixed $napart Value to return when testValue is an NA condition + * Note that this can be an array value to be returned * * @return mixed The value of errorpart or testValue determined by error condition + * If an array of values is passed as the $testValue argument, then the returned result will also be + * an array with the same dimensions */ public static function IFNA($testValue = '', $napart = '') { - $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue); - $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart); + if (is_array($testValue)) { + return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart); + } + + $napart = $napart ?? ''; return self::statementIf(Functions::isNa($testValue), $napart, $testValue); } @@ -156,6 +177,7 @@ public static function IFNA($testValue = '', $napart = '') * Value returned if corresponding testValue (nth) was true * * @param mixed ...$arguments Statement arguments + * Note that this can be an array value to be returned * * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true */ @@ -170,7 +192,7 @@ public static function IFS(...$arguments) $falseValueException = new Exception(); for ($i = 0; $i < $argumentCount; $i += 2) { $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]); - $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]); + $returnIfTrue = ($arguments[$i + 1] === null) ? '' : $arguments[$i + 1]; $result = self::statementIf($testValue, $returnIfTrue, $falseValueException); if ($result !== $falseValueException) { diff --git a/src/PhpSpreadsheet/Calculation/Logical/Operations.php b/src/PhpSpreadsheet/Calculation/Logical/Operations.php index 6bfb6a545b..7c83985960 100644 --- a/src/PhpSpreadsheet/Calculation/Logical/Operations.php +++ b/src/PhpSpreadsheet/Calculation/Logical/Operations.php @@ -2,11 +2,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Logical; +use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Operations { + use ArrayEnabled; + /** * LOGICAL_AND. * @@ -146,12 +149,17 @@ public static function logicalXor(...$args) * holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value * * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE + * Or can be an array of values * - * @return bool|string the boolean inverse of the argument + * @return array|bool|string the boolean inverse of the argument + * If an array of values is passed as an argument, then the returned result will also be an array + * with the same dimensions */ public static function NOT($logical = false) { - $logical = Functions::flattenSingleValue($logical); + if (is_array($logical)) { + return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical); + } if (is_string($logical)) { $logical = mb_strtoupper($logical, 'UTF-8'); diff --git a/tests/PhpSpreadsheetTests/Calculation/ArrayFormulaTest.php b/tests/PhpSpreadsheetTests/Calculation/ArrayFormulaTest.php index fcbe017e5d..ef8c398248 100644 --- a/tests/PhpSpreadsheetTests/Calculation/ArrayFormulaTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/ArrayFormulaTest.php @@ -55,6 +55,22 @@ public function providerArrayFormulae(): array '=SUM(SEQUENCE(3,3,0,1))', 36, ], + [ + '=IFERROR({5/2, 5/0}, MAX(ABS({-2,4,-6})))', + [[2.5, 6]], + ], + [ + '=MAX(IFERROR({5/2, 5/0}, 2.1))', + 2.5, + ], + [ + '=IF(FALSE,{1,2,3},{4,5,6})', + [[4, 5, 6]], + ], + [ + '=IFS(FALSE, {1,2,3}, TRUE, {4,5,6})', + [[4, 5, 6]], + ], ]; } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfErrorTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfErrorTest.php index 49e7d1e975..03ad8b3ae6 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfErrorTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfErrorTest.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Logical; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Logical; use PHPUnit\Framework\TestCase; @@ -30,4 +31,32 @@ public function providerIFERROR(): array { return require 'tests/data/Calculation/Logical/IFERROR.php'; } + + /** + * @dataProvider providerIfErrorArray + */ + public function testIfErrorArray(array $expectedResult, string $argument1, string $argument2): void + { + $calculation = Calculation::getInstance(); + + $formula = "=IFERROR({$argument1}, {$argument2})"; + $result = $calculation->_calculateFormulaValue($formula); + self::assertEquals($expectedResult, $result); + } + + public function providerIfErrorArray(): array + { + return [ + 'vector' => [ + [[2.5, 6]], + '{5/2, 5/0}', + 'MAX(ABS({-2,4,-6}))', + ], + 'return value' => [ + [[2.5, [[2, 3, 4]]]], + '{5/2, 5/0}', + '{2,3,4}', + ], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfNaTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfNaTest.php index ad3f07fc94..1c87de77cf 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfNaTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfNaTest.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Logical; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Logical; use PHPUnit\Framework\TestCase; @@ -30,4 +31,32 @@ public function providerIFNA(): array { return require 'tests/data/Calculation/Logical/IFNA.php'; } + + /** + * @dataProvider providerIfNaArray + */ + public function testIfNaArray(array $expectedResult, string $argument1, string $argument2): void + { + $calculation = Calculation::getInstance(); + + $formula = "=IFNA({$argument1}, {$argument2})"; + $result = $calculation->_calculateFormulaValue($formula); + self::assertEquals($expectedResult, $result); + } + + public function providerIfNaArray(): array + { + return [ + 'vector' => [ + [[2.5, '#DIV/0!', 6]], + '{5/2, 5/0, "#N/A"}', + 'MAX(ABS({-2,4,-6}))', + ], + 'return value' => [ + [[2.5, '#DIV/0!', [[2, 3, 4]]]], + '{5/2, 5/0, "#N/A"}', + '{2,3,4}', + ], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php index d236fdcce2..03eb15ba88 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Logical; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Logical; use PHPUnit\Framework\TestCase; @@ -28,4 +29,26 @@ public function providerNOT(): array { return require 'tests/data/Calculation/Logical/NOT.php'; } + + /** + * @dataProvider providerNotArray + */ + public function testNotArray(array $expectedResult, string $argument1): void + { + $calculation = Calculation::getInstance(); + + $formula = "=NOT({$argument1})"; + $result = $calculation->_calculateFormulaValue($formula); + self::assertEquals($expectedResult, $result); + } + + public function providerNotArray(): array + { + return [ + 'vector' => [ + [[false, true, true, false]], + '{TRUE, FALSE, FALSE, TRUE}', + ], + ]; + } } diff --git a/tests/data/Calculation/Logical/IFS.php b/tests/data/Calculation/Logical/IFS.php index e2aca0075c..f1b8649cc9 100644 --- a/tests/data/Calculation/Logical/IFS.php +++ b/tests/data/Calculation/Logical/IFS.php @@ -47,4 +47,11 @@ true, 'ABC', ], + 'array return' => [ + [[4, 5, 6]], + false, + [[1, 2, 3]], + true, + [[4, 5, 6]], + ], ]; diff --git a/tests/data/Calculation/Logical/SWITCH.php b/tests/data/Calculation/Logical/SWITCH.php index df3d6051ea..17dc31d811 100644 --- a/tests/data/Calculation/Logical/SWITCH.php +++ b/tests/data/Calculation/Logical/SWITCH.php @@ -39,6 +39,24 @@ 'DEF', 'Z', ], + 'Array return' => [ + [[4, 5, 6]], + 2, + 1, + [[1, 2, 3]], + 2, + [[4, 5, 6]], + [[7, 8, 9]], + ], + 'Array return as default' => [ + [[7, 8, 9]], + 3, + 1, + [[1, 2, 3]], + 2, + [[4, 5, 6]], + [[7, 8, 9]], + ], // Must be value - no parameter [ '#VALUE!',