Skip to content

Commit

Permalink
Improved method_exists support
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 3, 2018
1 parent df58b18 commit 1e63087
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 9 deletions.
14 changes: 6 additions & 8 deletions src/Type/Php/MethodExistsTypeSpecifyingExtension.php
Expand Up @@ -12,6 +12,7 @@
use PHPStan\Type\Accessory\HasMethodType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectWithoutClassType;

class MethodExistsTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
Expand Down Expand Up @@ -43,20 +44,17 @@ public function specifyTypes(
TypeSpecifierContext $context
): SpecifiedTypes
{
$objectExpr = $node->args[0]->value;
$objectType = $scope->getType($objectExpr);
if (!(new ObjectWithoutClassType())->isSuperTypeOf($objectType)->yes()) {
return new SpecifiedTypes([], []);
}

$methodNameType = $scope->getType($node->args[1]->value);
if (!$methodNameType instanceof ConstantStringType) {
return new SpecifiedTypes([], []);
}

return $this->typeSpecifier->create(
$objectExpr,
new HasMethodType($methodNameType->getValue()),
$node->args[0]->value,
new IntersectionType([
new ObjectWithoutClassType(),
new HasMethodType($methodNameType->getValue()),
]),
$context
);
}
Expand Down
16 changes: 16 additions & 0 deletions tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php
Expand Up @@ -365,6 +365,14 @@ public function testCallMethods(): void
'Call to an undefined method Test\Foo::lorem().',
911,
],
[
'Parameter #1 $callable of method Test\MethodExists::doBar() expects callable, array(object&hasMethod(foo), \'bar\') given.',
916,
],
[
'Parameter #1 $callable of method Test\MethodExists::doBar() expects callable, array(object&hasMethod(foo), \'bar\') given.',
921,
],
]);
}

Expand Down Expand Up @@ -562,6 +570,14 @@ public function testCallMethodsOnThisOnly(): void
'Parameter #1 $str of method Test\CheckDefaultArrayKeys::doBaz() expects string, int|string given.',
867,
],
[
'Parameter #1 $callable of method Test\MethodExists::doBar() expects callable, array(object&hasMethod(foo), \'bar\') given.',
916,
],
[
'Parameter #1 $callable of method Test\MethodExists::doBar() expects callable, array(object&hasMethod(foo), \'bar\') given.',
921,
],
]);
}

Expand Down
18 changes: 17 additions & 1 deletion tests/PHPStan/Rules/Methods/data/call-methods.php
Expand Up @@ -902,13 +902,29 @@ public function doFoo(\ReflectionType $type)
class MethodExists
{

public function doFoo(Foo $foo)
public function doFoo(Foo $foo, $mixed)
{
$foo->lorem();
if (method_exists($foo, 'lorem')) {
$foo->lorem();
}
$foo->lorem();

if (method_exists($mixed, 'foo')) {
$mixed->foo();
$this->doBar([$mixed, 'foo']);
$this->doBar([$mixed, 'bar']);
}

if (is_object($mixed) && method_exists($mixed, 'foo')) {
$this->doBar([$mixed, 'foo']);
$this->doBar([$mixed, 'bar']);
}
}

public function doBar(callable $callable)
{

}

}

0 comments on commit 1e63087

Please sign in to comment.