decouple VariableTypeResolver and TypeToStringResolver

This commit is contained in:
Tomas Votruba 2018-08-06 23:47:39 +02:00
parent 023270a1b3
commit 85aea7d8f8
3 changed files with 145 additions and 110 deletions

View File

@ -7,17 +7,9 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\Broker;
use PHPStan\TrinaryLogic;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\NodeAnalyzer\DocBlockAnalyzer;
use Rector\Node\Attribute;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
use Rector\NodeTypeResolver\PHPStan\Type\TypeToStringResolver;
final class NodeTypeResolver
{
@ -27,28 +19,13 @@ final class NodeTypeResolver
private $perNodeTypeResolvers = [];
/**
* @var ClassReflectionTypesResolver
* @var TypeToStringResolver
*/
private $classReflectionTypesResolver;
private $typeToStringResolver;
/**
* @var DocBlockAnalyzer
*/
private $docBlockAnalyzer;
/**
* @var Broker
*/
private $broker;
public function __construct(
ClassReflectionTypesResolver $classReflectionTypesResolver,
DocBlockAnalyzer $docBlockAnalyzer,
Broker $broker
) {
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
$this->docBlockAnalyzer = $docBlockAnalyzer;
$this->broker = $broker;
public function __construct(TypeToStringResolver $typeToStringResolver)
{
$this->typeToStringResolver = $typeToStringResolver;
}
public function addPerNodeTypeResolver(PerNodeTypeResolverInterface $perNodeTypeResolver): void
@ -74,11 +51,7 @@ final class NodeTypeResolver
return [$node->type->toString()];
}
if ($node instanceof Variable) {
return $this->resolveVariableNode($node, $nodeScope);
}
if ($node instanceof Expr) {
if ($node instanceof Expr && ! $node instanceof Variable) {
return $this->resolveExprNode($node);
}
@ -100,81 +73,6 @@ final class NodeTypeResolver
$type = $nodeScope->getType($exprNode);
return $this->resolveObjectTypesToStrings($type);
}
/**
* @todo make use of recursion
* @return string[]
*/
private function resolveObjectTypesToStrings(Type $type): array
{
$types = [];
if ($type instanceof ObjectType) {
$types[] = $type->getClassName();
}
if ($type instanceof UnionType) {
foreach ($type->getTypes() as $type) {
if ($type instanceof ObjectType) {
$types[] = $type->getClassName();
}
}
}
if ($type instanceof IntersectionType) {
foreach ($type->getTypes() as $type) {
if ($type instanceof ObjectType) {
$types[] = $type->getClassName();
}
}
}
return $types;
}
/**
* @return string[]
*/
private function resolveVariableNode(Variable $variableNode, Scope $nodeScope): array
{
$variableName = (string) $variableNode->name;
if ($nodeScope->hasVariableType($variableName) === TrinaryLogic::createYes()) {
$type = $nodeScope->getVariableType($variableName);
// this
if ($type instanceof ThisType) {
return $this->classReflectionTypesResolver->resolve($nodeScope->getClassReflection());
}
$types = $this->resolveObjectTypesToStrings($type);
// complete parents
foreach ($types as $type) {
$propertyClassReflection = $this->broker->getClass($type);
$types = array_merge($types, $this->classReflectionTypesResolver->resolve($propertyClassReflection));
}
return array_unique($types);
}
// get from annotation
$variableTypes = $this->docBlockAnalyzer->getVarTypes($variableNode);
foreach ($variableTypes as $i => $type) {
if (! class_exists($type)) {
unset($variableTypes[$i]);
continue;
}
$propertyClassReflection = $this->broker->getClass($type);
$variableTypes = array_merge(
$variableTypes,
$this->classReflectionTypesResolver->resolve($propertyClassReflection)
);
}
return array_unique($variableTypes);
return $this->typeToStringResolver->resolve($type);
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\PHPStan\Type;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
final class TypeToStringResolver
{
/**
* @return string[]
*/
public function resolve(Type $type): array
{
$types = [];
if ($type instanceof ObjectType) {
$types[] = $type->getClassName();
}
if ($type instanceof UnionType || $type instanceof IntersectionType) {
foreach ($type->getTypes() as $type) {
if ($type instanceof ObjectType) {
$types[] = $type->getClassName();
}
}
}
return $types;
}
}

View File

@ -0,0 +1,104 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PHPStan\Broker\Broker;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ThisType;
use Rector\BetterPhpDocParser\NodeAnalyzer\DocBlockAnalyzer;
use Rector\Node\Attribute;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\PHPStan\Type\TypeToStringResolver;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
final class VariableTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var ClassReflectionTypesResolver
*/
private $classReflectionTypesResolver;
/**
* @var DocBlockAnalyzer
*/
private $docBlockAnalyzer;
/**
* @var Broker
*/
private $broker;
/**
* @var TypeToStringResolver
*/
private $typeToStringResolver;
public function __construct(
ClassReflectionTypesResolver $classReflectionTypesResolver,
DocBlockAnalyzer $docBlockAnalyzer,
Broker $broker,
TypeToStringResolver $typeToStringResolver
) {
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
$this->docBlockAnalyzer = $docBlockAnalyzer;
$this->broker = $broker;
$this->typeToStringResolver = $typeToStringResolver;
}
/**
* @return string[]
*/
public function getNodeClasses(): array
{
return [Variable::class];
}
/**
* @param Variable $variableNode
* @return string[]
*/
public function resolve(Node $variableNode): array
{
$nodeScope = $variableNode->getAttribute(Attribute::SCOPE);
$variableName = (string) $variableNode->name;
if ($nodeScope->hasVariableType($variableName) === TrinaryLogic::createYes()) {
$type = $nodeScope->getVariableType($variableName);
// this
if ($type instanceof ThisType) {
return $this->classReflectionTypesResolver->resolve($nodeScope->getClassReflection());
}
$types = $this->typeToStringResolver->resolve($type);
// complete parents
foreach ($types as $type) {
$propertyClassReflection = $this->broker->getClass($type);
$types = array_merge($types, $this->classReflectionTypesResolver->resolve($propertyClassReflection));
}
return array_unique($types);
}
// get from annotation
$variableTypes = $this->docBlockAnalyzer->getVarTypes($variableNode);
foreach ($variableTypes as $i => $type) {
if (! class_exists($type)) {
unset($variableTypes[$i]);
continue;
}
$propertyClassReflection = $this->broker->getClass($type);
$variableTypes = array_merge(
$variableTypes,
$this->classReflectionTypesResolver->resolve($propertyClassReflection)
);
}
return array_unique($variableTypes);
}
}