From 9808b060995b57af161f87823db1d325fb6460c8 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 23 Jan 2026 17:38:10 +0100 Subject: [PATCH 1/5] ConstantArrayType: prevent unnecessary work --- src/Type/Constant/ConstantArrayType.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 599e5a026a..2d81d07327 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -564,9 +564,12 @@ public function findTypeAndMethodNames(): array $has->yes() && !$phpVersion->supportsCallableInstanceMethods() ) { - $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); - if ($classOrObject->isString()->yes() && !$methodReflection->isStatic()) { - continue; + if ($classOrObject->isString()->yes()) { + $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); + + if (!$methodReflection->isStatic()) { + continue; + } } } From c39f8e8432f8db87588237cda780330eb8a7e2bd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 23 Jan 2026 17:40:10 +0100 Subject: [PATCH 2/5] simplify --- src/Type/Constant/ConstantArrayType.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 2d81d07327..5cc90afeaf 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -563,13 +563,12 @@ public function findTypeAndMethodNames(): array if ( $has->yes() && !$phpVersion->supportsCallableInstanceMethods() + && $classOrObject->isString()->yes() ) { - if ($classOrObject->isString()->yes()) { - $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); + $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); - if (!$methodReflection->isStatic()) { - continue; - } + if (!$methodReflection->isStatic()) { + continue; } } From 17b439d6d6681a239c3d9fcf7de68866509da837 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 24 Jan 2026 11:33:30 +0100 Subject: [PATCH 3/5] added test --- src/Type/Constant/ConstantArrayType.php | 12 ++++++++---- .../Rules/Functions/CallCallablesRuleTest.php | 13 +++++++++++++ .../Rules/Functions/data/maybe-not-callable.php | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Rules/Functions/data/maybe-not-callable.php diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 5cc90afeaf..6cff7ef3af 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -563,12 +563,16 @@ public function findTypeAndMethodNames(): array if ( $has->yes() && !$phpVersion->supportsCallableInstanceMethods() - && $classOrObject->isString()->yes() ) { - $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); + $isString = $classOrObject->isString(); + if ($isString->yes()) { + $methodReflection = $type->getMethod($methodName->getValue(), new OutOfClassScope()); - if (!$methodReflection->isStatic()) { - continue; + if (!$methodReflection->isStatic()) { + continue; + } + } elseif ($isString->maybe()) { + $has = $has->and(TrinaryLogic::createMaybe()); } } diff --git a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php index 6c71a1a7c0..bd8ad74e49 100644 --- a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php @@ -353,4 +353,17 @@ public function testPipeOperator(): void ]); } + public function testMaybeNotCallable(): void + { + $errors = []; + if (PHP_VERSION_ID >= 80000) { + $errors[] = [ + "Trying to invoke array{'MaybeNotCallable\\\Bar'|\$this(MaybeNotCallable\Bar), 'doFoo'} but it might not be a callable.", + 15, + ]; + } + + $this->analyse([__DIR__ . '/data/maybe-not-callable.php'], $errors); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/maybe-not-callable.php b/tests/PHPStan/Rules/Functions/data/maybe-not-callable.php new file mode 100644 index 0000000000..47bed964c4 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/maybe-not-callable.php @@ -0,0 +1,17 @@ + Date: Sat, 24 Jan 2026 11:37:42 +0100 Subject: [PATCH 4/5] fix --- src/Type/Constant/ConstantArrayType.php | 2 +- tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 6cff7ef3af..2fef9327ad 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -572,7 +572,7 @@ public function findTypeAndMethodNames(): array continue; } } elseif ($isString->maybe()) { - $has = $has->and(TrinaryLogic::createMaybe()); + continue; } } diff --git a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php index bd8ad74e49..c08c0ded4a 100644 --- a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php @@ -358,7 +358,7 @@ public function testMaybeNotCallable(): void $errors = []; if (PHP_VERSION_ID >= 80000) { $errors[] = [ - "Trying to invoke array{'MaybeNotCallable\\\Bar'|\$this(MaybeNotCallable\Bar), 'doFoo'} but it might not be a callable.", + "Trying to invoke array{'MaybeNotCallable\\\Bar'|\$this(MaybeNotCallable\Bar), 'doFoo'} but it's not a callable.", 15, ]; } From bbb586b07887079fa6bc4ef67b0cb0276282d7c0 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 24 Jan 2026 11:38:13 +0100 Subject: [PATCH 5/5] Revert "fix" This reverts commit 64f367011edbcb5483b8d6bdc1a309598d935f6c. --- src/Type/Constant/ConstantArrayType.php | 2 +- tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 2fef9327ad..6cff7ef3af 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -572,7 +572,7 @@ public function findTypeAndMethodNames(): array continue; } } elseif ($isString->maybe()) { - continue; + $has = $has->and(TrinaryLogic::createMaybe()); } } diff --git a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php index c08c0ded4a..bd8ad74e49 100644 --- a/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php @@ -358,7 +358,7 @@ public function testMaybeNotCallable(): void $errors = []; if (PHP_VERSION_ID >= 80000) { $errors[] = [ - "Trying to invoke array{'MaybeNotCallable\\\Bar'|\$this(MaybeNotCallable\Bar), 'doFoo'} but it's not a callable.", + "Trying to invoke array{'MaybeNotCallable\\\Bar'|\$this(MaybeNotCallable\Bar), 'doFoo'} but it might not be a callable.", 15, ]; }