From baa179261fea4a40d2ced23a1fcb7a1bf07fef67 Mon Sep 17 00:00:00 2001 From: Dawid Warmuz Date: Mon, 13 Apr 2020 14:52:48 +0200 Subject: [PATCH 1/5] Add support for IFS() logical function --- .../Calculation/Calculation.php | 2 +- src/PhpSpreadsheet/Calculation/Logical.php | 36 +++++++++++++ .../Calculation/functionlist.txt | 1 + .../Calculation/Functions/Logical/IfsTest.php | 31 ++++++++++++ tests/data/Calculation/Logical/IFS.php | 50 +++++++++++++++++++ 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php create mode 100644 tests/data/Calculation/Logical/IFS.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 344bc32316..b39d1c7cac 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -1044,7 +1044,7 @@ class Calculation ], 'IFS' => [ 'category' => Category::CATEGORY_LOGICAL, - 'functionCall' => [Functions::class, 'DUMMY'], + 'functionCall' => [Logical::class, 'IFS'], 'argumentCount' => '2+', ], 'IMABS' => [ diff --git a/src/PhpSpreadsheet/Calculation/Logical.php b/src/PhpSpreadsheet/Calculation/Logical.php index 416d119ac8..1f8fb4b138 100644 --- a/src/PhpSpreadsheet/Calculation/Logical.php +++ b/src/PhpSpreadsheet/Calculation/Logical.php @@ -372,4 +372,40 @@ public static function IFNA($testValue = '', $napart = '') return self::statementIf(Functions::isNa($testValue), $napart, $testValue); } + + /** + * IFS. + * + * Excel Function: + * =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n) + * + * testValue1 ... testValue_n + * Conditions to Evaluate + * returnIfTrue1 ... returnIfTrue_n + * Value returned if corresponding testValue (nth) was true + * + * @category Logical Functions + * + * @param mixed ...$arguments Statement arguments + * + * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A error if none of testValues was true + */ + public static function IFS(...$arguments) + { + if (count($arguments) % 2 != 0) { + return Functions::NA(); + } + + for ($i = 0; $i < count($arguments); $i += 2) { + $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]); + $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]); + $result = self::statementIf($testValue, $returnIfTrue, Functions::DUMMY()); + + if ($result !== Functions::DUMMY()) { + return $result; + } + } + + return Functions::NA(); + } } diff --git a/src/PhpSpreadsheet/Calculation/functionlist.txt b/src/PhpSpreadsheet/Calculation/functionlist.txt index 7776e6ead4..ad92348e6b 100644 --- a/src/PhpSpreadsheet/Calculation/functionlist.txt +++ b/src/PhpSpreadsheet/Calculation/functionlist.txt @@ -161,6 +161,7 @@ HYPERLINK HYPGEOMDIST IF IFERROR +IFS IMABS IMAGINARY IMARGUMENT diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php new file mode 100644 index 0000000000..8c56881c1a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php @@ -0,0 +1,31 @@ +assertEquals($expectedResult, $result); + } + + public function providerIFS() + { + return require 'data/Calculation/Logical/IFS.php'; + } +} diff --git a/tests/data/Calculation/Logical/IFS.php b/tests/data/Calculation/Logical/IFS.php new file mode 100644 index 0000000000..d6e31bbd8c --- /dev/null +++ b/tests/data/Calculation/Logical/IFS.php @@ -0,0 +1,50 @@ + Date: Thu, 16 Apr 2020 09:53:26 +0200 Subject: [PATCH 2/5] Update IFS logical function reference on function lists --- docs/references/function-list-by-category.md | 2 +- docs/references/function-list-by-name.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/references/function-list-by-category.md b/docs/references/function-list-by-category.md index 9f76845918..02a930fd33 100644 --- a/docs/references/function-list-by-category.md +++ b/docs/references/function-list-by-category.md @@ -211,7 +211,7 @@ FALSE | \PhpOffice\PhpSpreadsheet\Calculation\Logical::FALSE IF | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementIf IFERROR | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFERROR IFNA | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFNA -IFS | **Not yet Implemented** +IFS | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFS NOT | \PhpOffice\PhpSpreadsheet\Calculation\Logical::NOT OR | \PhpOffice\PhpSpreadsheet\Calculation\Logical::logicalOr SWITCH | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementSwitch diff --git a/docs/references/function-list-by-name.md b/docs/references/function-list-by-name.md index 709b4b1da3..1356206fd4 100644 --- a/docs/references/function-list-by-name.md +++ b/docs/references/function-list-by-name.md @@ -210,7 +210,7 @@ Excel Function | Category | PhpSpreadsheet Function IF | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::statementIf IFERROR | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFERROR IFNA | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFNA -IFS | CATEGORY_LOGICAL | **Not yet Implemented** +IFS | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical::IFS IMABS | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMABS IMAGINARY | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMAGINARY IMARGUMENT | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering::IMARGUMENT From 1c2a7e8ee021e6a7f2ca00e104e09a21ac869421 Mon Sep 17 00:00:00 2001 From: Dawid Warmuz Date: Sat, 20 Jun 2020 17:20:16 +0200 Subject: [PATCH 3/5] Use Exception as false value in IFS logical function, so it never collides with string in spreadsheet --- src/PhpSpreadsheet/Calculation/Logical.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Logical.php b/src/PhpSpreadsheet/Calculation/Logical.php index 1f8fb4b138..d3cbe1ed68 100644 --- a/src/PhpSpreadsheet/Calculation/Logical.php +++ b/src/PhpSpreadsheet/Calculation/Logical.php @@ -395,13 +395,13 @@ public static function IFS(...$arguments) if (count($arguments) % 2 != 0) { return Functions::NA(); } - + $falseValueException = new Exception(); for ($i = 0; $i < count($arguments); $i += 2) { $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]); $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]); - $result = self::statementIf($testValue, $returnIfTrue, Functions::DUMMY()); + $result = self::statementIf($testValue, $returnIfTrue, $falseValueException); - if ($result !== Functions::DUMMY()) { + if ($result !== $falseValueException) { return $result; } } From 0061ca8ae9413873cb783053b266ed045829831a Mon Sep 17 00:00:00 2001 From: Dawid Warmuz Date: Sat, 20 Jun 2020 17:34:27 +0200 Subject: [PATCH 4/5] Update returning type of setUp method in IFS tests, so it is compatible with PHP 7.4 --- .../Calculation/Functions/Logical/IfsTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php index 8c56881c1a..b13d86330f 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php @@ -8,7 +8,7 @@ class IfsTest extends TestCase { - public function setUp() + public function setUp(): void { Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL); } @@ -26,6 +26,6 @@ public function testIFS($expectedResult, ...$args) public function providerIFS() { - return require 'data/Calculation/Logical/IFS.php'; + return require 'tests/data/Calculation/Logical/IFS.php'; } } From 0128adc7dcfee7fd4df627feba7fc9d24cbe5fd1 Mon Sep 17 00:00:00 2001 From: Dawid Warmuz Date: Sat, 20 Jun 2020 17:56:53 +0200 Subject: [PATCH 5/5] Add comment to explain use of Exception as false value in IFS logical function. This prevents possible string collision with false value in spreadsheet --- src/PhpSpreadsheet/Calculation/Logical.php | 5 ++--- .../Calculation/Functions/Logical/IfsTest.php | 7 ++++--- tests/data/Calculation/Logical/IFS.php | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Logical.php b/src/PhpSpreadsheet/Calculation/Logical.php index 452fabd541..69c543cef9 100644 --- a/src/PhpSpreadsheet/Calculation/Logical.php +++ b/src/PhpSpreadsheet/Calculation/Logical.php @@ -364,17 +364,16 @@ public static function IFNA($testValue = '', $napart = '') * returnIfTrue1 ... returnIfTrue_n * Value returned if corresponding testValue (nth) was true * - * @category Logical Functions - * * @param mixed ...$arguments Statement arguments * - * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A error if none of testValues was true + * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true */ public static function IFS(...$arguments) { if (count($arguments) % 2 != 0) { return Functions::NA(); } + // We use instance of Exception as a falseValue in order to prevent string collision with value in cell $falseValueException = new Exception(); for ($i = 0; $i < count($arguments); $i += 2) { $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php index b13d86330f..15687cd6a5 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfsTest.php @@ -8,7 +8,7 @@ class IfsTest extends TestCase { - public function setUp(): void + protected function setUp(): void { Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL); } @@ -17,11 +17,12 @@ public function setUp(): void * @dataProvider providerIFS * * @param mixed $expectedResult + * @param mixed $args */ - public function testIFS($expectedResult, ...$args) + public function testIFS($expectedResult, ...$args): void { $result = Logical::IFS(...$args); - $this->assertEquals($expectedResult, $result); + self::assertEquals($expectedResult, $result); } public function providerIFS() diff --git a/tests/data/Calculation/Logical/IFS.php b/tests/data/Calculation/Logical/IFS.php index d6e31bbd8c..e2aca0075c 100644 --- a/tests/data/Calculation/Logical/IFS.php +++ b/tests/data/Calculation/Logical/IFS.php @@ -13,7 +13,7 @@ '#N/A', false, 1, - true + true, ], [ 'ABC', @@ -27,7 +27,7 @@ false, 1, false, - 'ABC' + 'ABC', ], [ 'ABC', @@ -36,7 +36,7 @@ false, 1, true, - 'ABC' + 'ABC', ], [ false, @@ -45,6 +45,6 @@ false, 1, true, - 'ABC' + 'ABC', ], ];