Skip to content

Convert all relevant Logical functions to support array arguments #2600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Calculation/Logical.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
36 changes: 29 additions & 7 deletions src/PhpSpreadsheet/Calculation/Logical/Conditional.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;
}
Expand All @@ -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
*
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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
*/
Expand All @@ -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) {
Expand Down
12 changes: 10 additions & 2 deletions src/PhpSpreadsheet/Calculation/Logical/Operations.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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');
Expand Down
16 changes: 16 additions & 0 deletions tests/PhpSpreadsheetTests/Calculation/ArrayFormulaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]],
],
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}',
],
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}',
],
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}',
],
];
}
}
7 changes: 7 additions & 0 deletions tests/data/Calculation/Logical/IFS.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@
true,
'ABC',
],
'array return' => [
[[4, 5, 6]],
false,
[[1, 2, 3]],
true,
[[4, 5, 6]],
],
];
18 changes: 18 additions & 0 deletions tests/data/Calculation/Logical/SWITCH.php
Original file line number Diff line number Diff line change
Expand Up @@ -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!',
Expand Down