Skip to content

Commit

Permalink
Type-specifying extension for ReflectionClass::isSubclassOf()
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 21, 2021
1 parent f124a45 commit d3b5d60
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 2 deletions.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: PHPStan\Type\Php\ReflectionClassIsSubclassOfTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.methodTypeSpecifyingExtension

-
class: PHPStan\Type\Php\ReplaceFunctionsDynamicReturnTypeExtension
tags:
Expand Down
4 changes: 2 additions & 2 deletions src/Testing/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,8 @@ public function createTypeSpecifier(
$printer,
$reflectionProvider,
self::getContainer()->getServicesByTag(TypeSpecifierFactory::FUNCTION_TYPE_SPECIFYING_EXTENSION_TAG),
$methodTypeSpecifyingExtensions,
$staticMethodTypeSpecifyingExtensions
array_merge($methodTypeSpecifyingExtensions, self::getContainer()->getServicesByTag(TypeSpecifierFactory::METHOD_TYPE_SPECIFYING_EXTENSION_TAG)),
array_merge($staticMethodTypeSpecifyingExtensions, self::getContainer()->getServicesByTag(TypeSpecifierFactory::STATIC_METHOD_TYPE_SPECIFYING_EXTENSION_TAG)),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\MethodTypeSpecifyingExtension;
use PHPStan\Type\ObjectType;

class ReflectionClassIsSubclassOfTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
{

private TypeSpecifier $typeSpecifier;

public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
{
$this->typeSpecifier = $typeSpecifier;
}

public function getClass(): string
{
return \ReflectionClass::class;
}

public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
{
return $methodReflection->getName() === 'isSubclassOf'
&& isset($node->args[0])
&& $context->true();
}

public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
{
$valueType = $scope->getType($node->args[0]->value);
if (!$valueType instanceof ConstantStringType) {
return new SpecifiedTypes([], []);
}

return $this->typeSpecifier->create(
$node->var,
new GenericObjectType(\ReflectionClass::class, [
new ObjectType($valueType->getValue()),
]),
$context
);
}

}
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10892,6 +10892,11 @@ public function dataBug4573(): array
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4573.php');
}

public function dataBug4577(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4577.php');
}

/**
* @param string $file
* @return array<string, mixed[]>
Expand Down Expand Up @@ -11132,6 +11137,7 @@ private function gatherAssertTypes(string $file): array
* @dataProvider dataBug2112
* @dataProvider dataArrayMapClosure
* @dataProvider dataBug4573
* @dataProvider dataBug4577
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
18 changes: 18 additions & 0 deletions tests/PHPStan/Analyser/data/bug-4577.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Bug4577;

use function PHPStan\Analyser\assertType;

class Test
{

public function test(\ReflectionClass $refClass): void
{
if ($refClass->isSubclassOf(Test::class)) {
$instance = $refClass->newInstance();
assertType(Test::class, $instance);
}
}

}

0 comments on commit d3b5d60

Please sign in to comment.