Skip to content

Commit b271cbf

Browse files
authored
Merge pull request #4474 from oleibman/issue4451
Bug in resizeMatricesExtend
2 parents a70e4ec + 7efb118 commit b271cbf

File tree

4 files changed

+139
-10
lines changed

4 files changed

+139
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2929

3030
### Fixed
3131

32-
- Nothing yet.
32+
- Micro-optimization in getSheetByName. [PR #4499](https://github.com/PHPOffice/PhpSpreadsheet/pull/4499)
33+
- Bug in resizeMatricesExtend. [Issue #4451](https://github.com/PHPOffice/PhpSpreadsheet/issues/4451) [PR #4474](https://github.com/PHPOffice/PhpSpreadsheet/pull/4474)
3334

3435
## 2025-06-22 - 4.4.0
3536

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -852,15 +852,15 @@ private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, i
852852
if ($matrix2Columns < $matrix1Columns) {
853853
for ($i = 0; $i < $matrix2Rows; ++$i) {
854854
/** @var mixed[][] $matrix2 */
855-
$x = $matrix2[$i][$matrix2Columns - 1];
855+
$x = ($matrix2Columns === 1) ? $matrix2[$i][0] : null;
856856
for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
857857
$matrix2[$i][$j] = $x;
858858
}
859859
}
860860
}
861861
if ($matrix2Rows < $matrix1Rows) {
862-
$x = $matrix2[$matrix2Rows - 1];
863-
for ($i = 0; $i < $matrix1Rows; ++$i) {
862+
$x = ($matrix2Rows === 1) ? $matrix2[0] : array_fill(0, $matrix2Columns, null);
863+
for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
864864
$matrix2[$i] = $x;
865865
}
866866
}
@@ -870,15 +870,15 @@ private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, i
870870
if ($matrix1Columns < $matrix2Columns) {
871871
for ($i = 0; $i < $matrix1Rows; ++$i) {
872872
/** @var mixed[][] $matrix1 */
873-
$x = $matrix1[$i][$matrix1Columns - 1];
873+
$x = ($matrix1Columns === 1) ? $matrix1[$i][0] : null;
874874
for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
875875
$matrix1[$i][$j] = $x;
876876
}
877877
}
878878
}
879879
if ($matrix1Rows < $matrix2Rows) {
880-
$x = $matrix1[$matrix1Rows - 1];
881-
for ($i = 0; $i < $matrix2Rows; ++$i) {
880+
$x = ($matrix1Rows === 1) ? $matrix1[0] : array_fill(0, $matrix1Columns, null);
881+
for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
882882
$matrix1[$i] = $x;
883883
}
884884
}
@@ -2375,15 +2375,15 @@ private function executeNumericBinaryOperation(mixed $operand1, mixed $operand2,
23752375
for ($row = 0; $row < $rows; ++$row) {
23762376
for ($column = 0; $column < $columns; ++$column) {
23772377
/** @var mixed[][] $operand1 */
2378-
if ($operand1[$row][$column] === null) {
2378+
if (($operand1[$row][$column] ?? null) === null) {
23792379
$operand1[$row][$column] = 0;
23802380
} elseif (!self::isNumericOrBool($operand1[$row][$column])) {
23812381
$operand1[$row][$column] = self::makeError($operand1[$row][$column]);
23822382

23832383
continue;
23842384
}
23852385
/** @var mixed[][] $operand2 */
2386-
if ($operand2[$row][$column] === null) {
2386+
if (($operand2[$row][$column] ?? null) === null) {
23872387
$operand2[$row][$column] = 0;
23882388
} elseif (!self::isNumericOrBool($operand2[$row][$column])) {
23892389
$operand1[$row][$column] = self::makeError($operand2[$row][$column]);

src/PhpSpreadsheet/Calculation/LookupRef/Filter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private static function filterByRow(array $lookupArray, array $matchArray): arra
6262

6363
return array_filter(
6464
array_values($lookupArray),
65-
fn ($index): bool => (bool) $matchArray[$index],
65+
fn ($index): bool => (bool) ($matchArray[$index] ?? null),
6666
ARRAY_FILTER_USE_KEY
6767
);
6868
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
6+
7+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
8+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
9+
use PHPUnit\Framework\TestCase;
10+
use ReflectionMethod;
11+
12+
class Issue4451Test extends TestCase
13+
{
14+
public static function testReflect(): void
15+
{
16+
// Sample matrices to test with
17+
$matrix1 = [[1], [3]];
18+
$matrix2 = [[5], [8], [11]];
19+
20+
// Use reflection to make the protected method accessible
21+
$calculation = new Calculation();
22+
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesExtend');
23+
$reflectionMethod->setAccessible(true);
24+
25+
// Call the method using reflection
26+
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), 1, count($matrix2), 1]);
27+
28+
self::assertSame([[1], [3], [null]], $matrix1); //* @phpstan-ignore-line
29+
}
30+
31+
/**
32+
* These 2 tests are contrived. They prove that method
33+
* works as desired, but Excel will actually return
34+
* a CALC error, a result I don't know how to duplicate.
35+
*/
36+
public static function testExtendFirstColumn(): void
37+
{
38+
$spreadsheet = new Spreadsheet();
39+
$sheet = $spreadsheet->getActiveSheet();
40+
$sheet->setTitle('Products');
41+
$calculationEngine = Calculation::getInstance($spreadsheet);
42+
$calculationEngine->setInstanceArrayReturnType(
43+
Calculation::RETURN_ARRAY_AS_ARRAY
44+
);
45+
46+
$sheet->getCell('D5')->setValue(5);
47+
$sheet->getCell('E5')->setValue(20);
48+
$sheet->fromArray(
49+
[
50+
[5, 20, 'Apples'],
51+
[10, 20, 'Bananas'],
52+
[5, 20, 'Cherries'],
53+
[5, 40, 'Grapes'],
54+
[25, 50, 'Peaches'],
55+
[30, 60, 'Pears'],
56+
[35, 70, 'Papayas'],
57+
[40, 80, 'Mangos'],
58+
[null, 20, 'Unknown'],
59+
],
60+
null,
61+
'K1',
62+
true
63+
);
64+
$kRows = $sheet->getHighestDataRow('K');
65+
self::assertSame(8, $kRows);
66+
$lRows = $sheet->getHighestDataRow('L');
67+
self::assertSame(9, $lRows);
68+
$mRows = $sheet->getHighestDataRow('M');
69+
self::assertSame(9, $mRows);
70+
$sheet->getCell('A1')
71+
->setValue(
72+
"=FILTER(Products!M1:M$mRows,"
73+
. "(Products!K1:K$kRows=D5)"
74+
. "*(Products!L1:L$lRows=E5))"
75+
);
76+
77+
$result = $sheet->getCell('A1')->getCalculatedValue();
78+
self::assertSame([['Apples'], ['Cherries']], $result);
79+
$spreadsheet->disconnectWorksheets();
80+
}
81+
82+
public static function testExtendSecondColumn(): void
83+
{
84+
$spreadsheet = new Spreadsheet();
85+
$sheet = $spreadsheet->getActiveSheet();
86+
$sheet->setTitle('Products');
87+
$calculationEngine = Calculation::getInstance($spreadsheet);
88+
$calculationEngine->setInstanceArrayReturnType(
89+
Calculation::RETURN_ARRAY_AS_ARRAY
90+
);
91+
92+
$sheet->getCell('D5')->setValue(5);
93+
$sheet->getCell('E5')->setValue(20);
94+
$sheet->fromArray(
95+
[
96+
[5, 20, 'Apples'],
97+
[10, 20, 'Bananas'],
98+
[5, 20, 'Cherries'],
99+
[5, 40, 'Grapes'],
100+
[25, 50, 'Peaches'],
101+
[30, 60, 'Pears'],
102+
[35, 70, 'Papayas'],
103+
[40, 80, 'Mangos'],
104+
[null, 20, 'Unknown'],
105+
],
106+
null,
107+
'K1',
108+
true
109+
);
110+
$kRows = $sheet->getHighestDataRow('K');
111+
self::assertSame(8, $kRows);
112+
//$lRows = $sheet->getHighestDataRow('L');
113+
//self::assertSame(9, $lRows);
114+
$lRows = 2;
115+
$mRows = $sheet->getHighestDataRow('M');
116+
self::assertSame(9, $mRows);
117+
$sheet->getCell('A1')
118+
->setValue(
119+
"=FILTER(Products!M1:M$mRows,"
120+
. "(Products!K1:K$kRows=D5)"
121+
. "*(Products!L1:L$lRows=E5))"
122+
);
123+
124+
$result = $sheet->getCell('A1')->getCalculatedValue();
125+
self::assertSame([['Apples']], $result);
126+
$spreadsheet->disconnectWorksheets();
127+
}
128+
}

0 commit comments

Comments
 (0)