From bae1d01499d7533fa997a845ac089713bd777e64 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 10 Feb 2026 22:44:30 +0100 Subject: [PATCH 1/5] Fix GMP::toNumber --- src/Type/ObjectType.php | 5 ++- .../ParameterCastableToNumberRuleTest.php | 30 ++++++++++++++++ .../Rules/Functions/data/bug-13775.php | 34 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-13775.php diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 85fcc4bf05..8e295b9fc4 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -752,7 +752,10 @@ private function describeCache(): string public function toNumber(): Type { - if ($this->isInstanceOf('SimpleXMLElement')->yes()) { + if ( + $this->isInstanceOf('SimpleXMLElement')->yes() + || $this->isInstanceOf('GMP')->yes() + ) { return new UnionType([ new FloatType(), new IntegerType(), diff --git a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php index 588c845952..5b97cb28e2 100644 --- a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php @@ -122,6 +122,36 @@ public function testBug11883(): void ]); } + public function testBug13775(): void + { + $this->analyse([__DIR__ . '/data/bug-13775.php'], $this->hackPhp74ErrorMessages([ + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 13, + ], + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 19, + ], + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 22, + ], + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 25, + ], + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 28, + ], + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 31, + ], + ])); + } + public function testBug12146(): void { $this->analyse([__DIR__ . '/data/bug-12146.php'], $this->hackPhp74ErrorMessages([ diff --git a/tests/PHPStan/Rules/Functions/data/bug-13775.php b/tests/PHPStan/Rules/Functions/data/bug-13775.php new file mode 100644 index 0000000000..d265948a99 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-13775.php @@ -0,0 +1,34 @@ + Date: Wed, 11 Feb 2026 11:28:17 +0100 Subject: [PATCH 2/5] Improve --- src/Php/PhpVersion.php | 5 ++ .../ParameterCastableToNumberRule.php | 10 ++- .../ParameterCastableToNumberRuleTest.php | 67 ++++++++++++++++++- .../Rules/Functions/data/bug-13775-bis.php | 37 ++++++++++ .../Rules/Functions/data/bug-13775.php | 3 + 5 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-13775-bis.php diff --git a/src/Php/PhpVersion.php b/src/Php/PhpVersion.php index 6a555dff73..65db8bdae6 100644 --- a/src/Php/PhpVersion.php +++ b/src/Php/PhpVersion.php @@ -492,4 +492,9 @@ public function deprecatesIncOnNonNumericString(): bool return $this->versionId >= 80500; } + public function supportsObjectsInArraySumProduct(): bool + { + return $this->versionId >= 80300; + } + } diff --git a/src/Rules/Functions/ParameterCastableToNumberRule.php b/src/Rules/Functions/ParameterCastableToNumberRule.php index 640c73a440..3137688835 100644 --- a/src/Rules/Functions/ParameterCastableToNumberRule.php +++ b/src/Rules/Functions/ParameterCastableToNumberRule.php @@ -5,10 +5,12 @@ use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PHPStan\Analyser\Scope; +use PHPStan\Php\PhpVersion; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\ParameterCastableToStringCheck; use PHPStan\Rules\Rule; +use PHPStan\Type\ErrorType; use PHPStan\Type\Type; use function count; use function in_array; @@ -22,6 +24,7 @@ final class ParameterCastableToNumberRule implements Rule public function __construct( private ReflectionProvider $reflectionProvider, private ParameterCastableToStringCheck $parameterCastableToStringCheck, + private PhpVersion $phpVersion, ) { } @@ -63,11 +66,16 @@ public function processNode(Node $node, Scope $scope): array $errorMessage = 'Parameter %s of function %s expects an array of values castable to number, %s given.'; $functionParameters = $parametersAcceptor->getParameters(); + + $castFn = $this->phpVersion->supportsObjectsInArraySumProduct() + ? static fn (Type $t) => $t->toNumber() + : static fn (Type $t) => !$t->isObject()->no() ? new ErrorType() : $t->toNumber(); + $error = $this->parameterCastableToStringCheck->checkParameter( $origArgs[0], $scope, $errorMessage, - static fn (Type $t) => $t->toNumber(), + $castFn, $functionName, $this->parameterCastableToStringCheck->getParameterName( $origArgs[0], diff --git a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php index 5b97cb28e2..e4ebd5c149 100644 --- a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php @@ -2,6 +2,7 @@ namespace PHPStan\Rules\Functions; +use PHPStan\Php\PhpVersion; use PHPStan\Rules\ParameterCastableToStringCheck; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; @@ -20,7 +21,11 @@ class ParameterCastableToNumberRuleTest extends RuleTestCase protected function getRule(): Rule { $broker = self::createReflectionProvider(); - return new ParameterCastableToNumberRule($broker, new ParameterCastableToStringCheck(new RuleLevelHelper($broker, true, false, true, true, true, false, true))); + return new ParameterCastableToNumberRule( + $broker, + new ParameterCastableToStringCheck(new RuleLevelHelper($broker, true, false, true, true, true, false, true)), + self::getContainer()->getByType(PhpVersion::class), + ); } public function testRule(): void @@ -124,7 +129,7 @@ public function testBug11883(): void public function testBug13775(): void { - $this->analyse([__DIR__ . '/data/bug-13775.php'], $this->hackPhp74ErrorMessages([ + $errors = [ [ 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', 13, @@ -149,7 +154,63 @@ public function testBug13775(): void 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', 31, ], - ])); + [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 34, + ], + ]; + + if (PHP_VERSION_ID < 80300) { + $errors[] = [ + 'Parameter #1 $array of function array_product expects an array of values castable to number, array given.', + 37, + ]; + } + + $this->analyse([__DIR__ . '/data/bug-13775.php'], $this->hackPhp74ErrorMessages($errors)); + } + + public function testBug13775Bis(): void + { + $errors = [ + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 13, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 19, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 22, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 25, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 28, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 31, + ], + [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 34, + ], + ]; + + if (PHP_VERSION_ID < 80300) { + $errors[] = [ + 'Parameter #1 $array of function array_sum expects an array of values castable to number, array given.', + 37, + ]; + } + + $this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $this->hackPhp74ErrorMessages($errors)); } public function testBug12146(): void diff --git a/tests/PHPStan/Rules/Functions/data/bug-13775-bis.php b/tests/PHPStan/Rules/Functions/data/bug-13775-bis.php new file mode 100644 index 0000000000..69890599fa --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-13775-bis.php @@ -0,0 +1,37 @@ + Date: Wed, 11 Feb 2026 12:10:16 +0100 Subject: [PATCH 3/5] Fix --- .../Rules/Functions/ParameterCastableToNumberRuleTest.php | 4 ++-- .../Functions/data/param-castable-to-number-functions.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php index e4ebd5c149..ba1ea26bcb 100644 --- a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php @@ -167,7 +167,7 @@ public function testBug13775(): void ]; } - $this->analyse([__DIR__ . '/data/bug-13775.php'], $this->hackPhp74ErrorMessages($errors)); + $this->analyse([__DIR__ . '/data/bug-13775.php'], $errors); } public function testBug13775Bis(): void @@ -210,7 +210,7 @@ public function testBug13775Bis(): void ]; } - $this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $this->hackPhp74ErrorMessages($errors)); + $this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $errors); } public function testBug12146(): void diff --git a/tests/PHPStan/Rules/Functions/data/param-castable-to-number-functions.php b/tests/PHPStan/Rules/Functions/data/param-castable-to-number-functions.php index 9e7c5da4d2..b744bc35eb 100644 --- a/tests/PHPStan/Rules/Functions/data/param-castable-to-number-functions.php +++ b/tests/PHPStan/Rules/Functions/data/param-castable-to-number-functions.php @@ -40,6 +40,6 @@ function wrongNumberOfArguments(): void function validUsages(): void { - var_dump(array_sum(['5.5', false, true, new \SimpleXMLElement('7.7'), 5, 5.5, null])); - var_dump(array_product(['5.5', false, true, new \SimpleXMLElement('7.7'), 5, 5.5, null])); + var_dump(array_sum(['5.5', false, true, 5, 5.5, null])); + var_dump(array_product(['5.5', false, true, 5, 5.5, null])); } From c29d5cac8c59f3724928378cdc40def2c3fdfd6e Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 11 Feb 2026 12:14:57 +0100 Subject: [PATCH 4/5] Add non regression test --- .../Rules/Operators/InvalidBinaryOperationRuleTest.php | 7 +++++++ tests/PHPStan/Rules/Operators/data/bug-12123.php | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/PHPStan/Rules/Operators/data/bug-12123.php diff --git a/tests/PHPStan/Rules/Operators/InvalidBinaryOperationRuleTest.php b/tests/PHPStan/Rules/Operators/InvalidBinaryOperationRuleTest.php index bfca28ee4f..fb1b119745 100644 --- a/tests/PHPStan/Rules/Operators/InvalidBinaryOperationRuleTest.php +++ b/tests/PHPStan/Rules/Operators/InvalidBinaryOperationRuleTest.php @@ -795,6 +795,13 @@ public function testBenevolentUnion(): void ]); } + public function testBug12123(): void + { + $this->checkImplicitMixed = true; + + $this->analyse([__DIR__ . '/data/bug-12123.php'], []); + } + public function testBug7863(): void { $this->checkImplicitMixed = true; diff --git a/tests/PHPStan/Rules/Operators/data/bug-12123.php b/tests/PHPStan/Rules/Operators/data/bug-12123.php new file mode 100644 index 0000000000..13ba245955 --- /dev/null +++ b/tests/PHPStan/Rules/Operators/data/bug-12123.php @@ -0,0 +1,7 @@ + Date: Wed, 11 Feb 2026 12:25:40 +0100 Subject: [PATCH 5/5] Fix --- .../Rules/Functions/ParameterCastableToNumberRuleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php index ba1ea26bcb..e4ebd5c149 100644 --- a/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php @@ -167,7 +167,7 @@ public function testBug13775(): void ]; } - $this->analyse([__DIR__ . '/data/bug-13775.php'], $errors); + $this->analyse([__DIR__ . '/data/bug-13775.php'], $this->hackPhp74ErrorMessages($errors)); } public function testBug13775Bis(): void @@ -210,7 +210,7 @@ public function testBug13775Bis(): void ]; } - $this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $errors); + $this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $this->hackPhp74ErrorMessages($errors)); } public function testBug12146(): void