symplify PropertyTypeResolver

This commit is contained in:
Tomas Votruba 2018-08-13 23:25:54 +02:00
parent d91cf73b01
commit 45ca92729d
11 changed files with 100 additions and 72 deletions

View File

@ -5,10 +5,12 @@ namespace Rector\NodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\Broker;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverAwareInterface;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\TypeAttribute;
use Rector\NodeTypeResolver\PHPStan\Type\TypeToStringResolver;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
final class NodeTypeResolver
{
@ -22,9 +24,24 @@ final class NodeTypeResolver
*/
private $typeToStringResolver;
public function __construct(TypeToStringResolver $typeToStringResolver)
{
/**
* @var Broker
*/
private $broker;
/**
* @var ClassReflectionTypesResolver
*/
private $classReflectionTypesResolver;
public function __construct(
TypeToStringResolver $typeToStringResolver,
Broker $broker,
ClassReflectionTypesResolver $classReflectionTypesResolver
) {
$this->typeToStringResolver = $typeToStringResolver;
$this->broker = $broker;
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
}
public function addPerNodeTypeResolver(PerNodeTypeResolverInterface $perNodeTypeResolver): void
@ -43,6 +60,24 @@ final class NodeTypeResolver
* @return string[]
*/
public function resolve(Node $node): array
{
$types = $this->resolveFirstTypes($node);
if ($types === []) {
return $types;
}
// complete parent types - parent classes, interfaces and traits
foreach ($types as $type) {
$types += $this->classReflectionTypesResolver->resolve($this->broker->getClass($type));
}
return $types;
}
/**
* @return string[]
*/
private function resolveFirstTypes(Node $node): array
{
/** @var Scope|null $nodeScope */
$nodeScope = $node->getAttribute(TypeAttribute::SCOPE);

View File

@ -2,8 +2,10 @@
namespace Rector\NodeTypeResolver;
use PHPStan\Broker\Broker;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\PHPStan\Type\TypeToStringResolver;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -26,7 +28,14 @@ final class NodeTypeResolverFactory
{
/** @var TypeToStringResolver $typeToStringResolver */
$typeToStringResolver = $this->container->get(TypeToStringResolver::class);
$nodeTypeResolver = new NodeTypeResolver($typeToStringResolver);
/** @var Broker $broker */
$broker = $this->container->get(Broker::class);
/** @var ClassReflectionTypesResolver $classReflectionTypesResolver */
$classReflectionTypesResolver = $this->container->get(ClassReflectionTypesResolver::class);
$nodeTypeResolver = new NodeTypeResolver($typeToStringResolver, $broker, $classReflectionTypesResolver);
foreach ($this->container->getServiceIds() as $serviceId) {
if (! is_a($serviceId, PerNodeTypeResolverInterface::class, true)) {

View File

@ -3,46 +3,20 @@
namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Property;
use PHPStan\Broker\Broker;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverAwareInterface;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockAnalyzer;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
use Rector\Php\TypeAnalyzer;
use Rector\NodeTypeResolver\Node\TypeAttribute;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class PropertyTypeResolver implements PerNodeTypeResolverInterface
final class PropertyTypeResolver implements PerNodeTypeResolverInterface, NodeTypeResolverAwareInterface
{
/**
* @var ClassReflectionTypesResolver
* @var NodeTypeResolver
*/
private $classReflectionTypesResolver;
/**
* @var DocBlockAnalyzer
*/
private $docBlockAnalyzer;
/**
* @var Broker
*/
private $broker;
/**
* @var TypeAnalyzer
*/
private $typeAnalyzer;
public function __construct(
ClassReflectionTypesResolver $classReflectionTypesResolver,
DocBlockAnalyzer $docBlockAnalyzer,
Broker $broker,
TypeAnalyzer $typeAnalyzer
) {
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
$this->docBlockAnalyzer = $docBlockAnalyzer;
$this->broker = $broker;
$this->typeAnalyzer = $typeAnalyzer;
}
private $nodeTypeResolver;
/**
* @return string[]
@ -58,39 +32,15 @@ final class PropertyTypeResolver implements PerNodeTypeResolverInterface
*/
public function resolve(Node $propertyNode): array
{
// doc
$propertyTypes = $this->docBlockAnalyzer->getVarTypes($propertyNode);
if ($propertyTypes === []) {
return [];
}
// fake property to local PropertyFetch → PHPStan understand that
$propertyFetchNode = new PropertyFetch(new Variable('this'), (string) $propertyNode->props[0]->name);
$propertyFetchNode->setAttribute(TypeAttribute::SCOPE, $propertyNode->getAttribute(TypeAttribute::SCOPE));
$propertyTypes = $this->filterOutScalarTypes($propertyTypes);
foreach ($propertyTypes as $propertyType) {
$propertyClassReflection = $this->broker->getClass($propertyType);
$propertyTypes += $this->classReflectionTypesResolver->resolve($propertyClassReflection);
}
return $propertyTypes;
return $this->nodeTypeResolver->resolve($propertyFetchNode);
}
/**
* @param string[] $propertyTypes
* @return string[]
*/
private function filterOutScalarTypes(array $propertyTypes): array
public function setNodeTypeResolver(NodeTypeResolver $nodeTypeResolver): void
{
foreach ($propertyTypes as $key => $type) {
if (! $this->typeAnalyzer->isPhpReservedType($type)) {
continue;
}
unset($propertyTypes[$key]);
}
if ($propertyTypes === ['null']) {
return [];
}
return $propertyTypes;
$this->nodeTypeResolver = $nodeTypeResolver;
}
}

View File

@ -5,6 +5,7 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyTypeResolver
use Iterator;
use PhpParser\Node\Stmt\Property;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyTypeResolver\Source\ClassThatExtendsHtml;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyTypeResolver\Source\Html;
/**
@ -25,5 +26,6 @@ final class PropertyTypeResolverTest extends AbstractNodeTypeResolverTest
public function provideData(): Iterator
{
yield [__DIR__ . '/Source/ClassWithProperties.php', 0, [Html::class]];
yield [__DIR__ . '/Source/ClassWithProperties.php', 1, [ClassThatExtendsHtml::class, Html::class]];
}
}

View File

@ -0,0 +1,9 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyTypeResolver\Source;
class ClassThatExtendsHtml extends
Html
{
}

View File

@ -8,4 +8,9 @@ final class MethodParamDocBlock
* @var Html
*/
public $htmlProperty;
/**
* @var ClassThatExtendsHtml
*/
public $anotherHtmlProperty;
}

View File

@ -2,7 +2,7 @@
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyTypeResolver\Source;
final class Html
class Html
{
}

View File

@ -1,6 +1,8 @@
<?php declare(strict_types=1);
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\ProductRepository;
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\SomeProduct;
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\SomeRequest;
final class SomeController
{
@ -16,8 +18,7 @@ final class SomeController
{
$products = $this->productRepository->fetchAll();
}
public function detail(Request $request, Product $product)
public function detail(SomeRequest $request, SomeProduct $product)
{
}
}

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source;
final class SomeProduct
{
}

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source;
final class SomeRequest
{
}

View File

@ -1,6 +1,8 @@
<?php declare(strict_types=1);
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\ProductRepository;
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\SomeProduct;
use Rector\Tests\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector\Source\SomeRequest;
final class SomeController
{
@ -8,8 +10,7 @@ final class SomeController
{
$products = $productRepository->fetchAll();
}
public function detail(Request $request, Product $product)
public function detail(SomeRequest $request, SomeProduct $product)
{
}
}