Skip to content

Commit 4791209

Browse files
authored
Merge pull request #4382 from oleibman/issue4381
Handle #REF! As Argument to COUNTIF, AVERAGEIF, SUMIF
2 parents 027a5c6 + 44ed8a8 commit 4791209

File tree

5 files changed

+83
-7
lines changed

5 files changed

+83
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
3333
### Fixed
3434

3535
- Refactor Helper/Html. [PR #4359](https://github.com/PHPOffice/PhpSpreadsheet/pull/4359)
36+
- Handle #REF! as Argument to AVERAGEIF/COUNTIF/SUMIF. [Issue #4381](https://github.com/PHPOffice/PhpSpreadsheet/issues/4381) [PR #4382](https://github.com/PHPOffice/PhpSpreadsheet/pull/4382)
3637
- Ignore ignoredErrors when not applicable. [Issue #4375](https://github.com/PHPOffice/PhpSpreadsheet/issues/4375) [PR #4377](https://github.com/PHPOffice/PhpSpreadsheet/pull/4377)
3738
- Better handling of defined names on sheets whose titles include apostrophes. [Issue #4356](https://github.com/PHPOffice/PhpSpreadsheet/issues/4356) [Issue #4362](https://github.com/PHPOffice/PhpSpreadsheet/issues/4362) [Issue #4376](https://github.com/PHPOffice/PhpSpreadsheet/issues/4376) [PR #4360](https://github.com/PHPOffice/PhpSpreadsheet/pull/4360)
3839
- Partial solution for removing rows or columns that include edge ranges. [Issue #1449](https://github.com/PHPOffice/PhpSpreadsheet/issues/1449) [PR #3528](https://github.com/PHPOffice/PhpSpreadsheet/pull/3528)

src/PhpSpreadsheet/Calculation/Statistical/Conditional.php

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpOffice\PhpSpreadsheet\Calculation\Database\DSum;
1010
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
1111
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
12+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
1213

1314
class Conditional
1415
{
@@ -24,13 +25,18 @@ class Conditional
2425
* Excel Function:
2526
* AVERAGEIF(range,condition[, average_range])
2627
*
27-
* @param mixed $range Data values
28+
* @param mixed $range Data values, expect array
2829
* @param null|array|string $condition the criteria that defines which cells will be checked
2930
* @param mixed $averageRange Data values
3031
*/
3132
public static function AVERAGEIF(mixed $range, null|array|string $condition, mixed $averageRange = []): null|int|float|string
3233
{
3334
if (!is_array($range) || !is_array($averageRange) || array_key_exists(0, $range) || array_key_exists(0, $averageRange)) {
35+
$refError = ExcelError::REF();
36+
if (in_array($refError, [$range, $averageRange], true)) {
37+
return $refError;
38+
}
39+
3440
throw new CalcException('Must specify range of cells, not any kind of literal');
3541
}
3642
$database = self::databaseFromRangeAndValue($range, $averageRange);
@@ -76,11 +82,21 @@ public static function AVERAGEIFS(mixed ...$args): null|int|float|string
7682
* Excel Function:
7783
* COUNTIF(range,condition)
7884
*
79-
* @param mixed[] $range Data values
85+
* @param mixed $range Data values, expect array
8086
* @param null|array|string $condition the criteria that defines which cells will be counted
8187
*/
82-
public static function COUNTIF(array $range, null|array|string $condition): string|int
88+
public static function COUNTIF(mixed $range, null|array|string $condition): string|int
8389
{
90+
if (
91+
!is_array($range)
92+
|| array_key_exists(0, $range)
93+
) {
94+
if ($range === ExcelError::REF()) {
95+
return $range;
96+
}
97+
98+
throw new CalcException('Must specify range of cells, not any kind of literal');
99+
}
84100
// Filter out any empty values that shouldn't be included in a COUNT
85101
$range = array_filter(
86102
Functions::flattenArray($range),
@@ -169,10 +185,24 @@ public static function MINIFS(mixed ...$args): null|float|string
169185
* Excel Function:
170186
* SUMIF(range, criteria, [sum_range])
171187
*
172-
* @param array $range Data values
188+
* @param mixed $range Data values, expecting array
189+
* @param mixed $sumRange Data values, expecting array
173190
*/
174-
public static function SUMIF(array $range, mixed $condition, array $sumRange = []): null|float|string
191+
public static function SUMIF(mixed $range, mixed $condition, mixed $sumRange = []): null|float|string
175192
{
193+
if (
194+
!is_array($range)
195+
|| array_key_exists(0, $range)
196+
|| !is_array($sumRange)
197+
|| array_key_exists(0, $sumRange)
198+
) {
199+
$refError = ExcelError::REF();
200+
if (in_array($refError, [$range, $sumRange], true)) {
201+
return $refError;
202+
}
203+
204+
throw new CalcException('Must specify range of cells, not any kind of literal');
205+
}
176206
$database = self::databaseFromRangeAndValue($range, $sumRange);
177207
$condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
178208

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\MathTrig;
66

7+
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
78
use PHPUnit\Framework\Attributes\DataProvider;
89

910
class SumIfTest extends AllSetupTeardown
@@ -38,4 +39,22 @@ public static function providerSUMIF(): array
3839
{
3940
return require 'tests/data/Calculation/MathTrig/SUMIF.php';
4041
}
42+
43+
public function testOutliers(): void
44+
{
45+
$sheet = $this->getSheet();
46+
$sheet->getCell('A1')->setValue('=SUMIF(5,"<32")');
47+
48+
try {
49+
$sheet->getCell('A1')->getCalculatedValue();
50+
self::fail('Should receive exception for non-array arg');
51+
} catch (CalcException $e) {
52+
self::assertStringContainsString('Must specify range of cells', $e->getMessage());
53+
}
54+
55+
$sheet->getCell('A4')->setValue('=SUMIF(#REF!,"<32")');
56+
self::assertSame('#REF!', $sheet->getCell('A4')->getCalculatedValue());
57+
$sheet->getCell('A5')->setValue('=SUMIF(D1:D4, 1, #REF!)');
58+
self::assertSame('#REF!', $sheet->getCell('A5')->getCalculatedValue());
59+
}
4160
}

tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/AverageIfTest.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
66

77
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
8+
use PHPUnit\Framework\Attributes\DataProvider;
89

910
class AverageIfTest extends AllSetupTeardown
1011
{
11-
#[\PHPUnit\Framework\Attributes\DataProvider('providerAVERAGEIF')]
12+
#[DataProvider('providerAVERAGEIF')]
1213
public function testAVERAGEIF(mixed $expectedResult, mixed ...$args): void
1314
{
1415
$this->runTestCaseNoBracket('AVERAGEIF', $expectedResult, ...$args);
@@ -41,5 +42,11 @@ public function testOutliers(): void
4142
}
4243
$sheet->getCell('A3')->setValue('=AVERAGEIF(C1:C1,"<32")');
4344
self::assertSame(5, $sheet->getCell('A3')->getCalculatedValue(), 'first arg is single cell');
45+
46+
$sheet->getCell('A4')->setValue('=AVERAGEIF(#REF!,1)');
47+
self::assertSame('#REF!', $sheet->getCell('A4')->getCalculatedValue());
48+
49+
$sheet->getCell('A5')->setValue('=AVERAGEIF(D1:D4, 1, #REF!)');
50+
self::assertSame('#REF!', $sheet->getCell('A5')->getCalculatedValue());
4451
}
4552
}

tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/CountIfTest.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
66

7+
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
710
class CountIfTest extends AllSetupTeardown
811
{
9-
#[\PHPUnit\Framework\Attributes\DataProvider('providerCOUNTIF')]
12+
#[DataProvider('providerCOUNTIF')]
1013
public function testCOUNTIF(mixed $expectedResult, mixed ...$args): void
1114
{
1215
$this->runTestCaseNoBracket('COUNTIF', $expectedResult, ...$args);
@@ -27,4 +30,20 @@ public static function providerCOUNTIF(): array
2730
{
2831
return require 'tests/data/Calculation/Statistical/COUNTIF.php';
2932
}
33+
34+
public function testOutliers(): void
35+
{
36+
$sheet = $this->getSheet();
37+
$sheet->getCell('A1')->setValue('=COUNTIF(5,"<32")');
38+
39+
try {
40+
$sheet->getCell('A1')->getCalculatedValue();
41+
self::fail('Should receive exception for non-array arg');
42+
} catch (CalcException $e) {
43+
self::assertStringContainsString('Must specify range of cells', $e->getMessage());
44+
}
45+
46+
$sheet->getCell('A4')->setValue('=COUNTIF(#REF!,1)');
47+
self::assertSame('#REF!', $sheet->getCell('A4')->getCalculatedValue());
48+
}
3049
}

0 commit comments

Comments
 (0)