Skip to content

Commit 0c5808e

Browse files
authored
Merge pull request #4372 from oleibman/stanbleed04
Phpstan Bleeding Edge Part 4: Calculation
2 parents b3452af + 09eacd3 commit 0c5808e

File tree

17 files changed

+44
-199
lines changed

17 files changed

+44
-199
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,5 @@
11
parameters:
22
ignoreErrors:
3-
-
4-
message: "#^Call to function is_array\\(\\) with array will always evaluate to true\\.$#"
5-
count: 2
6-
path: src/PhpSpreadsheet/Calculation/Calculation.php
7-
8-
-
9-
message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#"
10-
count: 1
11-
path: src/PhpSpreadsheet/Calculation/Calculation.php
12-
13-
-
14-
message: "#^Offset 'reference' might not exist on array\\|null\\.$#"
15-
count: 1
16-
path: src/PhpSpreadsheet/Calculation/Calculation.php
17-
18-
-
19-
message: "#^Offset 'type' might not exist on array\\|null\\.$#"
20-
count: 2
21-
path: src/PhpSpreadsheet/Calculation/Calculation.php
22-
23-
-
24-
message: "#^Offset 'value' might not exist on array\\|null\\.$#"
25-
count: 7
26-
path: src/PhpSpreadsheet/Calculation/Calculation.php
27-
28-
-
29-
message: "#^PHPDoc tag @var with type int is not subtype of native type int\\<48, 57\\>\\|int\\<256, 1048576\\>\\|numeric\\-string\\.$#"
30-
count: 1
31-
path: src/PhpSpreadsheet/Calculation/Calculation.php
32-
33-
-
34-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function max expects non\\-empty\\-array, list\\<\\(int\\|string\\)\\> given\\.$#"
35-
count: 1
36-
path: src/PhpSpreadsheet/Calculation/Calculation.php
37-
38-
-
39-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function max expects non\\-empty\\-array, list\\<int\\> given\\.$#"
40-
count: 2
41-
path: src/PhpSpreadsheet/Calculation/Calculation.php
42-
43-
-
44-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function max expects non\\-empty\\-array, list\\<string\\> given\\.$#"
45-
count: 1
46-
path: src/PhpSpreadsheet/Calculation/Calculation.php
47-
48-
-
49-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function min expects non\\-empty\\-array, list\\<\\(int\\|string\\)\\> given\\.$#"
50-
count: 1
51-
path: src/PhpSpreadsheet/Calculation/Calculation.php
52-
53-
-
54-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function min expects non\\-empty\\-array, list\\<int\\> given\\.$#"
55-
count: 2
56-
path: src/PhpSpreadsheet/Calculation/Calculation.php
57-
58-
-
59-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function min expects non\\-empty\\-array, list\\<string\\> given\\.$#"
60-
count: 1
61-
path: src/PhpSpreadsheet/Calculation/Calculation.php
62-
63-
-
64-
message: "#^Offset 0 on non\\-empty\\-list\\<string\\> on left side of \\?\\? always exists and is not nullable\\.$#"
65-
count: 1
66-
path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
67-
68-
-
69-
message: "#^Parameter &\\$year by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Date\\:\\:adjustYearMonth\\(\\) expects int, float given\\.$#"
70-
count: 2
71-
path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
72-
73-
-
74-
message: "#^Strict comparison using \\!\\=\\= between DateInterval and false will always evaluate to true\\.$#"
75-
count: 1
76-
path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
77-
78-
-
79-
message: "#^Parameter &\\$testVal3 by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustYear\\(\\) expects string, float\\|int given\\.$#"
80-
count: 1
81-
path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
82-
83-
-
84-
message: "#^Parameter &\\$hour by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Time\\:\\:adjustMinute\\(\\) expects int, float given\\.$#"
85-
count: 2
86-
path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
87-
88-
-
89-
message: "#^Parameter &\\$minute by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Time\\:\\:adjustSecond\\(\\) expects int, float given\\.$#"
90-
count: 2
91-
path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
92-
93-
-
94-
message: "#^Parameter &\\$operand by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engine\\\\FormattedNumber\\:\\:convertToNumberIfCurrency\\(\\) expects string, float given\\.$#"
95-
count: 1
96-
path: src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
97-
98-
-
99-
message: "#^Parameter &\\$operand by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engine\\\\FormattedNumber\\:\\:convertToNumberIfNumeric\\(\\) expects string, float given\\.$#"
100-
count: 1
101-
path: src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
102-
103-
-
104-
message: "#^Parameter &\\$operand by\\-ref type of method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engine\\\\FormattedNumber\\:\\:convertToNumberIfPercent\\(\\) expects string, float given\\.$#"
105-
count: 1
106-
path: src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
107-
108-
-
109-
message: "#^Strict comparison using \\!\\=\\= between float and null will always evaluate to true\\.$#"
110-
count: 1
111-
path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
112-
113-
-
114-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function max expects non\\-empty\\-array, array given\\.$#"
115-
count: 1
116-
path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
117-
118-
-
119-
message: "#^Parameter \\#1 \\.\\.\\.\\$arg1 of function min expects non\\-empty\\-array, array given\\.$#"
120-
count: 1
121-
path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
122-
123-
-
124-
message: "#^Call to function is_array\\(\\) with array will always evaluate to true\\.$#"
125-
count: 1
126-
path: src/PhpSpreadsheet/Calculation/Information/Value.php
127-
128-
-
129-
message: "#^Parameter \\#1 \\$array \\(list\\<mixed\\>\\) of array_values is already a list, call has no effect\\.$#"
130-
count: 1
131-
path: src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
132-
133-
-
134-
message: "#^Offset float does not exist on non\\-empty\\-list\\<mixed\\>\\.$#"
135-
count: 1
136-
path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php
137-
138-
-
139-
message: "#^Call to function is_numeric\\(\\) with float\\|int\\|numeric\\-string will always evaluate to true\\.$#"
140-
count: 1
141-
path: src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
142-
143-
-
144-
message: "#^Call to function is_float\\(\\) with float will always evaluate to true\\.$#"
145-
count: 2
146-
path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
147-
148-
-
149-
message: "#^Offset float does not exist on non\\-empty\\-list\\<mixed\\>\\.$#"
150-
count: 4
151-
path: src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
152-
153-
-
154-
message: "#^PHPDoc tag @var with type array is not subtype of native type list\\<mixed\\>\\|false\\.$#"
155-
count: 2
156-
path: src/PhpSpreadsheet/Calculation/TextData/Text.php
157-
1583
-
1594
message: "#^Call to function is_string\\(\\) with numeric\\-string\\|non\\-empty\\-string will always evaluate to true\\.$#"
1605
count: 1

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4426,11 +4426,10 @@ private function internalParseFormula(string $formula, ?Cell $cell = null): bool
44264426
if (ctype_digit($val) && $val <= 1048576) {
44274427
// Row range
44284428
$stackItemType = 'Row Reference';
4429-
/** @var int $valx */
44304429
$valx = $val;
44314430
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : AddressRange::MAX_COLUMN; // Max 16,384 columns for Excel2007
44324431
$val = "{$rangeWS2}{$endRowColRef}{$val}";
4433-
} elseif (ctype_alpha($val) && is_string($val) && strlen($val) <= 3) {
4432+
} elseif (ctype_alpha($val) && strlen($val) <= 3) {
44344433
// Column range
44354434
$stackItemType = 'Column Reference';
44364435
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : AddressRange::MAX_ROW; // Max 1,048,576 rows for Excel2007
@@ -4564,7 +4563,7 @@ private function internalParseFormula(string $formula, ?Cell $cell = null): bool
45644563

45654564
while (($op = $stack->pop()) !== null) {
45664565
// pop everything off the stack and push onto output
4567-
if ((is_array($op) && $op['value'] == '(')) {
4566+
if ($op['value'] == '(') {
45684567
return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
45694568
}
45704569
$output[] = $op;
@@ -4821,7 +4820,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
48214820
if ($breakNeeded) {
48224821
break;
48234822
}
4824-
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
4823+
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); // @phpstan-ignore-line
48254824
if ($pCellParent !== null && $this->spreadsheet !== null) {
48264825
$cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($sheet1), false);
48274826
} else {
@@ -4919,8 +4918,8 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
49194918
$this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
49204919
$stack->push('Error', ExcelError::null(), null);
49214920
} else {
4922-
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':'
4923-
. Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
4921+
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' // @phpstan-ignore-line
4922+
. Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); // @phpstan-ignore-line
49244923
$this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
49254924
$stack->push('Value', $cellIntersect, $cellRef);
49264925
}
@@ -5060,6 +5059,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
50605059
}
50615060

50625061
$functionName = $matches[1];
5062+
/** @var array $argCount */
50635063
$argCount = $stack->pop();
50645064
$argCount = $argCount['value'];
50655065
if ($functionName !== 'MKMATRIX') {
@@ -5090,9 +5090,10 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
50905090
&& (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a]))
50915091
&& (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
50925092
) {
5093+
/** @var array $arg */
50935094
if ($arg['reference'] === null) {
50945095
$nextArg = $cellID;
5095-
if ($functionName === 'ISREF' && is_array($arg) && ($arg['type'] ?? '') === 'Value') {
5096+
if ($functionName === 'ISREF' && ($arg['type'] ?? '') === 'Value') {
50965097
if (array_key_exists('value', $arg)) {
50975098
$argValue = $arg['value'];
50985099
if (is_scalar($argValue)) {
@@ -5113,6 +5114,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
51135114
}
51145115
}
51155116
} else {
5117+
/** @var array $arg */
51165118
if ($arg['type'] === 'Empty Argument' && in_array($functionName, ['MIN', 'MINA', 'MAX', 'MAXA', 'IF'], true)) {
51175119
$emptyArguments[] = false;
51185120
$args[] = $arg['value'] = 0;
@@ -5235,6 +5237,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
52355237
if ($stack->count() != 1) {
52365238
return $this->raiseFormulaError('internal error');
52375239
}
5240+
/** @var array $output */
52385241
$output = $stack->pop();
52395242
$output = $output['value'];
52405243

@@ -5287,6 +5290,7 @@ private function executeArrayComparison(mixed $operand1, mixed $operand2, string
52875290
foreach ($operand1 as $x => $operandData) {
52885291
$this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2));
52895292
$this->executeBinaryComparisonOperation($operandData, $operand2, $operation, $stack);
5293+
/** @var array $r */
52905294
$r = $stack->pop();
52915295
$result[$x] = $r['value'];
52925296
}
@@ -5295,6 +5299,7 @@ private function executeArrayComparison(mixed $operand1, mixed $operand2, string
52955299
foreach ($operand2 as $x => $operandData) {
52965300
$this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operand1), $operation, $this->showValue($operandData));
52975301
$this->executeBinaryComparisonOperation($operand1, $operandData, $operation, $stack);
5302+
/** @var array $r */
52985303
$r = $stack->pop();
52995304
$result[$x] = $r['value'];
53005305
}
@@ -5306,6 +5311,7 @@ private function executeArrayComparison(mixed $operand1, mixed $operand2, string
53065311
foreach ($operand1 as $x => $operandData) {
53075312
$this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2[$x]));
53085313
$this->executeBinaryComparisonOperation($operandData, $operand2[$x], $operation, $stack, true);
5314+
/** @var array $r */
53095315
$r = $stack->pop();
53105316
$result[$x] = $r['value'];
53115317
}

src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private static function buildQuery(array $criteriaNames, array $criteria): strin
108108
}
109109

110110
$rowQuery = array_map(
111-
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
111+
fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''), // @phpstan-ignore-line
112112
$baseQuery
113113
);
114114

src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,11 @@ private static function adjustYearMonth(int &$year, int &$month, int $baseYear):
151151
if ($month < 1) {
152152
// Handle year/month adjustment if month < 1
153153
--$month;
154-
$year += ceil($month / 12) - 1;
154+
$year += (int) (ceil($month / 12) - 1);
155155
$month = 13 - abs($month % 12);
156156
} elseif ($month > 12) {
157157
// Handle year/month adjustment if month > 12
158-
$year += floor($month / 12);
158+
$year += intdiv($month, 12);
159159
$month = ($month % 12);
160160
}
161161

src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static function between(array|DateTimeInterface|float|int|string $endDate
5050

5151
$days = ExcelError::VALUE();
5252
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
53-
if ($diff !== false && !is_bool($diff->days)) {
53+
if (!is_bool($diff->days)) {
5454
$days = $diff->days;
5555
if ($diff->invert) {
5656
$days = -$days;

src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static function adjustYear(string $testVal1, string $testVal2, string &$t
119119
if (!is_numeric($testVal1) || $testVal1 < 31) {
120120
if (!is_numeric($testVal2) || $testVal2 < 12) {
121121
if (is_numeric($testVal3) && $testVal3 < 12) {
122-
$testVal3 += 2000;
122+
$testVal3 = (string) ($testVal3 + 2000);
123123
}
124124
}
125125
}

src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,27 +87,27 @@ public static function fromHMS(array|int|float|bool|null|string $hour, array|int
8787
private static function adjustSecond(int &$second, int &$minute): void
8888
{
8989
if ($second < 0) {
90-
$minute += floor($second / 60);
90+
$minute += (int) floor($second / 60);
9191
$second = 60 - abs($second % 60);
9292
if ($second == 60) {
9393
$second = 0;
9494
}
9595
} elseif ($second >= 60) {
96-
$minute += floor($second / 60);
96+
$minute += intdiv($second, 60);
9797
$second = $second % 60;
9898
}
9999
}
100100

101101
private static function adjustMinute(int &$minute, int &$hour): void
102102
{
103103
if ($minute < 0) {
104-
$hour += floor($minute / 60);
104+
$hour += (int) floor($minute / 60);
105105
$minute = 60 - abs($minute % 60);
106106
if ($minute == 60) {
107107
$minute = 0;
108108
}
109109
} elseif ($minute >= 60) {
110-
$hour += floor($minute / 60);
110+
$hour += intdiv($minute, 60);
111111
$minute = $minute % 60;
112112
}
113113
}

src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ public static function convertToNumberIfFormatted(string &$operand): bool
4444
* Identify whether a string contains a numeric value,
4545
* and convert it to a numeric if it is.
4646
*
47-
* @param string $operand string value to test
47+
* @param float|string $operand string value to test
4848
*/
49-
public static function convertToNumberIfNumeric(string &$operand): bool
49+
public static function convertToNumberIfNumeric(float|string &$operand): bool
5050
{
5151
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
52-
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
52+
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim("$operand"));
5353
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
5454
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
5555

@@ -86,12 +86,12 @@ public static function convertToNumberIfFraction(string &$operand): bool
8686
* Identify whether a string contains a percentage, and if so,
8787
* convert it to a numeric.
8888
*
89-
* @param string $operand string value to test
89+
* @param float|string $operand string value to test
9090
*/
91-
public static function convertToNumberIfPercent(string &$operand): bool
91+
public static function convertToNumberIfPercent(float|string &$operand): bool
9292
{
9393
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
94-
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
94+
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim("$operand"));
9595
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
9696
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
9797

@@ -111,13 +111,13 @@ public static function convertToNumberIfPercent(string &$operand): bool
111111
* Identify whether a string contains a currency value, and if so,
112112
* convert it to a numeric.
113113
*
114-
* @param string $operand string value to test
114+
* @param float|string $operand string value to test
115115
*/
116-
public static function convertToNumberIfCurrency(string &$operand): bool
116+
public static function convertToNumberIfCurrency(float|string &$operand): bool
117117
{
118118
$currencyRegexp = self::currencyMatcherRegexp();
119119
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
120-
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
120+
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', "$operand");
121121

122122
$match = [];
123123
if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {

0 commit comments

Comments
 (0)