From bfd7b652df171ffe6868d8b11c3cf4aa1db00e74 Mon Sep 17 00:00:00 2001 From: Christopher Wosinski Date: Tue, 12 Nov 2019 10:37:15 +0100 Subject: [PATCH] Fixes #1482: method_exists() with string --- .../MethodExistsTypeSpecifyingExtension.php | 9 +++++ ...mpossibleCheckTypeFunctionCallRuleTest.php | 34 +++++++------------ .../data/check-type-function-call.php | 3 ++ 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/Type/Php/MethodExistsTypeSpecifyingExtension.php b/src/Type/Php/MethodExistsTypeSpecifyingExtension.php index e25e7d915e..53ba46461a 100644 --- a/src/Type/Php/MethodExistsTypeSpecifyingExtension.php +++ b/src/Type/Php/MethodExistsTypeSpecifyingExtension.php @@ -13,7 +13,9 @@ use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\FunctionTypeSpecifyingExtension; use PHPStan\Type\IntersectionType; +use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\StringType; class MethodExistsTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension { @@ -44,6 +46,13 @@ public function specifyTypes( TypeSpecifierContext $context ): SpecifiedTypes { + $objectType = $scope->getType($node->args[0]->value); + if (!$objectType instanceof ObjectType) { + if ((new StringType())->isSuperTypeOf($objectType)->yes()) { + return new SpecifiedTypes([], []); + } + } + $methodNameType = $scope->getType($node->args[1]->value); if (!$methodNameType instanceof ConstantStringType) { return new SpecifiedTypes([], []); diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 6935415a93..12ab5d3e61 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -137,10 +137,6 @@ public function testImpossibleCheckTypeFunctionCall(): void 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'testWithStringFirst…\' will always evaluate to true.', 585, ], - [ - 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'undefinedMethod\' will always evaluate to false.', - 588, - ], [ 'Call to function method_exists() with \'UndefinedClass\' and string will always evaluate to false.', 594, @@ -151,43 +147,43 @@ public function testImpossibleCheckTypeFunctionCall(): void ], [ 'Call to function method_exists() with CheckTypeFunctionCall\MethodExists and \'testWithNewObjectIn…\' will always evaluate to true.', - 606, + 609, ], [ 'Call to function method_exists() with $this(CheckTypeFunctionCall\MethodExistsWithTrait) and \'method\' will always evaluate to true.', - 621, + 624, ], [ 'Call to function method_exists() with $this(CheckTypeFunctionCall\MethodExistsWithTrait) and \'someAnother\' will always evaluate to true.', - 624, + 627, ], [ 'Call to function method_exists() with $this(CheckTypeFunctionCall\MethodExistsWithTrait) and \'unknown\' will always evaluate to false.', - 627, + 630, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'method\' will always evaluate to true.', - 630, + 633, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'someAnother\' will always evaluate to true.', - 633, + 636, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'unknown\' will always evaluate to false.', - 636, + 639, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'method\' will always evaluate to true.', - 639, + 642, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'someAnother\' will always evaluate to true.', - 642, + 645, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'unknown\' will always evaluate to false.', - 645, + 648, ], ] ); @@ -259,10 +255,6 @@ public function testImpossibleCheckTypeFunctionCallWithoutAlwaysTrue(): void 'Call to function is_callable() with mixed will always evaluate to false.', 571, ], - [ - 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'undefinedMethod\' will always evaluate to false.', - 588, - ], [ 'Call to function method_exists() with \'UndefinedClass\' and string will always evaluate to false.', 594, @@ -273,15 +265,15 @@ public function testImpossibleCheckTypeFunctionCallWithoutAlwaysTrue(): void ], [ 'Call to function method_exists() with $this(CheckTypeFunctionCall\MethodExistsWithTrait) and \'unknown\' will always evaluate to false.', - 627, + 630, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'unknown\' will always evaluate to false.', - 636, + 639, ], [ 'Call to function method_exists() with \'CheckTypeFunctionCa…\' and \'unknown\' will always evaluate to false.', - 645, + 648, ], ] ); diff --git a/tests/PHPStan/Rules/Comparison/data/check-type-function-call.php b/tests/PHPStan/Rules/Comparison/data/check-type-function-call.php index 7ca65409fe..6ea1bb6676 100644 --- a/tests/PHPStan/Rules/Comparison/data/check-type-function-call.php +++ b/tests/PHPStan/Rules/Comparison/data/check-type-function-call.php @@ -596,6 +596,9 @@ public function testWithStringFirstArgument(): void if (method_exists('UndefinedClass', 'test')) { } + + if (method_exists($string, 'test')) { + } } public function testWithNewObjectInFirstArgument(): void