From 5c39df5b5bd99b9ca0ae3ac81bc62a4b92ab873c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 23 Jul 2025 10:42:56 +0200 Subject: [PATCH 1/2] Fix MethodExistsTypeSpecifyingExtension for union types --- .../MethodExistsTypeSpecifyingExtension.php | 14 +++++++++----- tests/PHPStan/Analyser/nsrt/bug-13272.php | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-13272.php diff --git a/src/Type/Php/MethodExistsTypeSpecifyingExtension.php b/src/Type/Php/MethodExistsTypeSpecifyingExtension.php index 3db54618e5..67e57a6cd2 100644 --- a/src/Type/Php/MethodExistsTypeSpecifyingExtension.php +++ b/src/Type/Php/MethodExistsTypeSpecifyingExtension.php @@ -14,7 +14,6 @@ use PHPStan\Type\Accessory\HasMethodType; use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\FunctionTypeSpecifyingExtension; use PHPStan\Type\IntersectionType; use PHPStan\Type\ObjectWithoutClassType; @@ -50,8 +49,8 @@ public function specifyTypes( TypeSpecifierContext $context, ): SpecifiedTypes { - $methodNameType = $scope->getType($node->getArgs()[1]->value); - if (!$methodNameType instanceof ConstantStringType) { + $methodNameTypes = $scope->getType($node->getArgs()[1]->value)->getConstantStrings(); + if ($methodNameTypes === []) { return $this->typeSpecifier->create( new FuncCall(new FullyQualified('method_exists'), $node->getRawArgs()), new ConstantBooleanType(true), @@ -60,6 +59,11 @@ public function specifyTypes( ); } + $hasMethodTypes = []; + foreach ($methodNameTypes as $methodNameType) { + $hasMethodTypes[] = new HasMethodType($methodNameType->getValue()); + } + $objectType = $scope->getType($node->getArgs()[0]->value); if ($objectType->isString()->yes()) { if ($objectType->isClassString()->yes()) { @@ -67,7 +71,7 @@ public function specifyTypes( $node->getArgs()[0]->value, new IntersectionType([ $objectType, - new HasMethodType($methodNameType->getValue()), + ...$hasMethodTypes, ]), $context, $scope, @@ -82,7 +86,7 @@ public function specifyTypes( new UnionType([ new IntersectionType([ new ObjectWithoutClassType(), - new HasMethodType($methodNameType->getValue()), + ...$hasMethodTypes, ]), new ClassStringType(), ]), diff --git a/tests/PHPStan/Analyser/nsrt/bug-13272.php b/tests/PHPStan/Analyser/nsrt/bug-13272.php new file mode 100644 index 0000000000..327937f694 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13272.php @@ -0,0 +1,19 @@ + Date: Wed, 23 Jul 2025 10:46:05 +0200 Subject: [PATCH 2/2] Update phpstan-baseline.neon --- phpstan-baseline.neon | 6 ------ 1 file changed, 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 47101ea9fe..fdae76ab53 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1617,12 +1617,6 @@ parameters: count: 1 path: src/Type/Php/MbSubstituteCharacterDynamicReturnTypeExtension.php - - - message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#' - identifier: phpstanApi.instanceofType - count: 1 - path: src/Type/Php/MethodExistsTypeSpecifyingExtension.php - - message: '#^Doing instanceof PHPStan\\Type\\ConstantScalarType is error\-prone and deprecated\. Use Type\:\:isConstantScalarValue\(\) or Type\:\:getConstantScalarTypes\(\) or Type\:\:getConstantScalarValues\(\) instead\.$#' identifier: phpstanApi.instanceofType