Skip to content

Commit 7614467

Browse files
committed
Fix for Issue 2158 (AverageIf Calculation Problem)
Issue PHPOffice#2158 reports an error calculating AverageIf because a function returns null rather than a string. There turn out to be several components to this problem: - The nominal fix to the problem is to add some null-to-nullstring coercion in DatabaseAbstract. - This fixes the error, but does not necessarily lead to the correct result because buildQuery treats values of null and null-string identically, whereas Excel does not. So change that to treat null-string as any other string. - But that doesn't lead to the correct result either. That's because Functions/ifCondition recognizes a null string, but then continues to (over-)process it until it returns the wrong result. Fix this problem in conjunction with the other two, and we finally get the correct result. A new unit test is added for AVERAGEIF, and new test cases are added for SUMIF. In each case, there are complementary tests for conditions of null and null-string, and the results agree with Excel. There may or may not be value in adding new tests to other functions, and I will be glad to do so for any functions which you care to identify, but no existing tests broke as a result of these changes.
1 parent fea56e1 commit 7614467

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static function buildQuery(array $criteriaNames, array $criteria): strin
9595
foreach ($criteria as $key => $criterion) {
9696
foreach ($criterion as $field => $value) {
9797
$criterionName = $criteriaNames[$field];
98-
if ($value !== null && $value !== '') {
98+
if ($value !== null) {
9999
$condition = self::buildCondition($value, $criterionName);
100100
$baseQuery[$key][] = $condition;
101101
}
@@ -104,12 +104,12 @@ private static function buildQuery(array $criteriaNames, array $criteria): strin
104104

105105
$rowQuery = array_map(
106106
function ($rowValue) {
107-
return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : $rowValue[0];
107+
return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
108108
},
109109
$baseQuery
110110
);
111111

112-
return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : $rowQuery[0];
112+
return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
113113
}
114114

115115
private static function buildCondition($criterion, string $criterionName): string

src/PhpSpreadsheet/Calculation/Functions.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ public static function ifCondition($condition)
251251
$condition = self::flattenSingleValue($condition);
252252

253253
if ($condition === '') {
254-
$condition = '=""';
254+
return '=""';
255255
}
256256
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
257257
$condition = self::operandSpecialHandling($condition);

tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumIfTest.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public function testSUMIF2($expectedResult, array $array1, $condition, ?array $a
2020
$sheet->fromArray($array1, null, 'A1', true);
2121
$maxARow = count($array1);
2222
$firstArg = "A1:A$maxARow";
23-
//$secondArg = is_string($condition) ? "\"$condition\"" : $condition;
24-
$sheet->getCell('B1')->setValue($condition);
23+
$this->setCell('B1', $condition);
2524
$secondArg = 'B1';
2625
if (empty($array2)) {
2726
$sheet->getCell('D1')->setValue("=SUMIF($firstArg, $secondArg)");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
4+
5+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class AverageIf2Test extends TestCase
9+
{
10+
public function testAVERAGEIF(): void
11+
{
12+
$table = [
13+
['n', 1],
14+
['', 2],
15+
['y', 3],
16+
['', 4],
17+
['n', 5],
18+
['n', 6],
19+
['n', 7],
20+
['', 8],
21+
[null, 9],
22+
];
23+
24+
$spreadsheet = new Spreadsheet();
25+
$sheet = $spreadsheet->getActiveSheet();
26+
$sheet->fromArray($table, null, 'A1', true);
27+
$sheet->getCell('C1')->setValue('=AVERAGEIF(A:A,"",B:B)');
28+
$sheet->getCell('C2')->setValue('=AVERAGEIF(A:A,,B:B)');
29+
$sheet->getCell('C3')->setValue('=AVERAGEIF(A:A,"n",B:B)');
30+
$sheet->getCell('C4')->setValue('=AVERAGEIF(A:A,"y",B:B)');
31+
$sheet->getCell('C5')->setValue('=AVERAGEIF(A:A,"x",B:B)');
32+
33+
self::assertEqualsWithDelta(5.75, $sheet->getCell('C1')->getCalculatedValue(), 1E-12);
34+
self::assertEquals('#DIV/0!', $sheet->getCell('C2')->getCalculatedValue());
35+
self::assertEqualsWithDelta(4.75, $sheet->getCell('C3')->getCalculatedValue(), 1E-12);
36+
self::assertEqualsWithDelta(3, $sheet->getCell('C4')->getCalculatedValue(), 1E-12);
37+
self::assertEquals('#DIV/0!', $sheet->getCell('C5')->getCalculatedValue());
38+
$spreadsheet->disconnectWorksheets();
39+
}
40+
}

tests/data/Calculation/MathTrig/SUMIF.php

+18
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,22 @@
150150
'North ?',
151151
[[36693], [22100], ['=3/0'], [34440], [29889], [50090], [32080], [45500]],
152152
],
153+
[
154+
23,
155+
[['n'], [''], ['y'], [''], ['n'], ['n'], ['n'], [''], []],
156+
'',
157+
[[1], [2], [3], [4], [5], [6], [7], [8], [9]],
158+
],
159+
[
160+
0,
161+
[['n'], [''], ['y'], [''], ['n'], ['n'], ['n'], [''], []],
162+
null,
163+
[[1], [2], [3], [4], [5], [6], [7], [8], [9]],
164+
],
165+
[
166+
19,
167+
[['n'], [''], ['y'], [''], ['n'], ['n'], ['n'], [''], []],
168+
'n',
169+
[[1], [2], [3], [4], [5], [6], [7], [8], [9]],
170+
],
153171
];

0 commit comments

Comments
 (0)