decouple ClassLikeType analyzing logic to AbstractClassLikeTypeResolver

This commit is contained in:
Tomas Votruba 2018-05-31 01:59:16 +02:00
parent c311ee4436
commit d6ffcb8434
6 changed files with 131 additions and 167 deletions

View File

@ -0,0 +1,104 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TraitUse;
use Rector\BetterReflection\Reflector\SmartClassReflector;
use Rector\Node\Attribute;
abstract class AbstractClassLikeTypeResolver
{
/**
* @var SmartClassReflector
*/
private $smartClassReflector;
/**
* @required
*/
public function setSmartClassReflector(SmartClassReflector $smartClassReflector): void
{
$this->smartClassReflector = $smartClassReflector;
}
/**
* @param Name|ClassLike $node
*/
protected function resolveNameNode(Node $node): string
{
$name = (string) $node->getAttribute(Attribute::CLASS_NAME);
if ($name) {
return $name;
}
$namespacedName = $node->getAttribute(Attribute::NAMESPACED_NAME);
if ($namespacedName instanceof FullyQualified) {
return $namespacedName->toString();
}
$nameNode = $node->getAttribute(Attribute::RESOLVED_NAME);
if ($nameNode instanceof Name) {
return $nameNode->toString();
}
if ($node instanceof Name) {
return $node->toString();
}
return (string) $node->name;
}
/**
* @param Class_|Interface_ $classLikeNode
* @return string[]
*/
protected function resolveExtendsTypes(ClassLike $classLikeNode, ?string $className = null): array
{
if (! $classLikeNode->extends) {
return [];
}
return $this->smartClassReflector->getClassParents($className, $classLikeNode);
}
/**
* @param Class_|Trait_ $classOrTraitNode
* @return string[]
*/
protected function resolveUsedTraitTypes(ClassLike $classOrTraitNode): array
{
$usedTraits = [];
foreach ($classOrTraitNode->stmts as $stmt) {
if (! $stmt instanceof TraitUse) {
continue;
}
foreach ($stmt->traits as $trait) {
if ($trait->hasAttribute(Attribute::RESOLVED_NAME)) {
$usedTraits[] = (string) $trait->getAttribute(Attribute::RESOLVED_NAME);
}
}
}
return $usedTraits;
}
/**
* @return string[]
*/
protected function resolveImplementsTypes(Class_ $classNode): array
{
return array_map(function (Name $interface): string {
/** @var FullyQualified $interface */
return $interface->toString();
}, $classNode->implements);
}
}

View File

@ -4,22 +4,10 @@ namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use Rector\NodeAnalyzer\ClassLikeAnalyzer;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
final class ClassTypeResolver implements PerNodeTypeResolverInterface
final class ClassTypeResolver extends AbstractClassLikeTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var ClassLikeAnalyzer
*/
private $classLikeAnalyzer;
public function __construct(ClassLikeAnalyzer $classLikeAnalyzer)
{
$this->classLikeAnalyzer = $classLikeAnalyzer;
}
/**
* @return string[]
*/
@ -29,11 +17,18 @@ final class ClassTypeResolver implements PerNodeTypeResolverInterface
}
/**
* @param ClassLike $classLikeNode
* @param Class_ $classNode
* @return string[]
*/
public function resolve(Node $classLikeNode): array
public function resolve(Node $classNode): array
{
return $this->classLikeAnalyzer->resolveTypeAndParentTypes($classLikeNode);
$className = $this->resolveNameNode($classNode);
$types[] = $className;
$types = array_merge($types, $this->resolveExtendsTypes($classNode, $className));
$types = array_merge($types, $this->resolveImplementsTypes($classNode));
$types = array_merge($types, $this->resolveUsedTraitTypes($classNode));
return $types;
}
}

View File

@ -4,21 +4,10 @@ namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Stmt\Interface_;
use Rector\NodeAnalyzer\ClassLikeAnalyzer;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
final class InterfaceTypeResolver implements PerNodeTypeResolverInterface
final class InterfaceTypeResolver extends AbstractClassLikeTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var ClassLikeAnalyzer
*/
private $classLikeAnalyzer;
public function __construct(ClassLikeAnalyzer $classLikeAnalyzer)
{
$this->classLikeAnalyzer = $classLikeAnalyzer;
}
/**
* @return string[]
*/
@ -33,8 +22,8 @@ final class InterfaceTypeResolver implements PerNodeTypeResolverInterface
*/
public function resolve(Node $interfaceNode): array
{
$className = $this->classLikeAnalyzer->resolveNameNode($interfaceNode);
$className = $this->resolveNameNode($interfaceNode);
return array_merge([$className], $this->classLikeAnalyzer->resolveExtendsTypes($interfaceNode, $className));
return array_merge([$className], $this->resolveExtendsTypes($interfaceNode, $className));
}
}

View File

@ -4,21 +4,10 @@ namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Stmt\Trait_;
use Rector\NodeAnalyzer\ClassLikeAnalyzer;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
final class TraitTypeResolver implements PerNodeTypeResolverInterface
final class TraitTypeResolver extends AbstractClassLikeTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var ClassLikeAnalyzer
*/
private $classLikeAnalyzer;
public function __construct(ClassLikeAnalyzer $classLikeAnalyzer)
{
$this->classLikeAnalyzer = $classLikeAnalyzer;
}
/**
* @return string[]
*/
@ -33,8 +22,8 @@ final class TraitTypeResolver implements PerNodeTypeResolverInterface
*/
public function resolve(Node $traitNode): array
{
$types[] = $this->classLikeAnalyzer->resolveNameNode($traitNode);
$types = array_merge($types, $this->classLikeAnalyzer->resolveUsedTraitTypes($traitNode));
$types[] = $this->resolveNameNode($traitNode);
$types = array_merge($types, $this->resolveUsedTraitTypes($traitNode));
return $types;
}

View File

@ -17,7 +17,7 @@ final class Attribute
public const TYPES = 'types';
/**
* System name.
* Internal php-parser name.
* Do not change this even if you want!
*
* @var string
@ -25,7 +25,7 @@ final class Attribute
public const ORIGINAL_NODE = 'origNode';
/**
* System name to be found in @see \PhpParser\NodeVisitor\NameResolver
* Internal php-parser name. @see \PhpParser\NodeVisitor\NameResolver
* Do not change this even if you want!
*
* @var string
@ -106,4 +106,12 @@ final class Attribute
* @var string
*/
public const COMMENTS = 'comments';
/**
* Internal php-parser name.
* Do not change this even if you want!
*
* @var string
*/
public const NAMESPACED_NAME = 'namespacedName';
}

View File

@ -3,35 +3,14 @@
namespace Rector\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TraitUse;
use Rector\BetterReflection\Reflector\SmartClassReflector;
use Rector\Node\Attribute;
/**
* Read-only utils for ClassLike|Class_|Trait_|Interface_ Node:
* "class" SomeClass, "interface" Interface, "trait" Trait
*
* @todo decouple to Class_|Trait_|Interface_ TypeResolvers and remove this class
* This is used nowhere else
*/
final class ClassLikeAnalyzer
{
/**
* @var SmartClassReflector
*/
private $smartClassReflector;
public function __construct(SmartClassReflector $smartClassReflector)
{
$this->smartClassReflector = $smartClassReflector;
}
public function isAnonymousClassNode(Node $node): bool
{
return $node instanceof Class_ && $node->isAnonymous();
@ -41,104 +20,4 @@ final class ClassLikeAnalyzer
{
return $node instanceof Class_ && ! $node->isAnonymous();
}
/**
* @return string[]
*/
public function resolveTypeAndParentTypes(ClassLike $classLikeNode): array
{
$types = [];
if (! $this->isAnonymousClassNode($classLikeNode)) {
$className = $this->resolveNameNode($classLikeNode);
$types[] = $className;
if ($classLikeNode instanceof Class_ || $classLikeNode instanceof Interface_) {
$types = array_merge($types, $this->resolveExtendsTypes($classLikeNode, $className));
}
} else {
$types = array_merge($types, $this->resolveExtendsTypes($classLikeNode));
}
if ($classLikeNode instanceof Class_) {
$types = array_merge($types, $this->resolveImplementsTypes($classLikeNode));
$types = array_merge($types, $this->resolveUsedTraitTypes($classLikeNode));
}
return $types;
}
/**
* @param Name|ClassLike $node
*/
public function resolveNameNode(Node $node): string
{
$name = (string) $node->getAttribute(Attribute::CLASS_NAME);
if ($name) {
return $name;
}
$namespacedName = $node->getAttribute('namespacedName');
if ($namespacedName instanceof FullyQualified) {
return $namespacedName->toString();
}
$nameNode = $node->getAttribute(Attribute::RESOLVED_NAME);
if ($nameNode instanceof Name) {
return $nameNode->toString();
}
if ($node instanceof Name) {
return $node->toString();
}
return (string) $node->name;
}
/**
* @param Class_|Interface_ $classLikeNode
* @return string[]
*/
public function resolveExtendsTypes(ClassLike $classLikeNode, ?string $className = null): array
{
if (! $classLikeNode->extends) {
return [];
}
return $this->smartClassReflector->getClassParents($className, $classLikeNode);
}
/**
* @param Class_|Trait_ $classOrTraitNode
* @return string[]
*/
public function resolveUsedTraitTypes(ClassLike $classOrTraitNode): array
{
$usedTraits = [];
foreach ($classOrTraitNode->stmts as $stmt) {
if (! $stmt instanceof TraitUse) {
continue;
}
foreach ($stmt->traits as $trait) {
if ($trait->hasAttribute(Attribute::RESOLVED_NAME)) {
$usedTraits[] = (string) $trait->getAttribute(Attribute::RESOLVED_NAME);
}
}
}
return $usedTraits;
}
/**
* @return string[]
*/
private function resolveImplementsTypes(Class_ $classNode): array
{
return array_map(function (Name $interface): string {
/** @var FullyQualified $interface */
return $interface->toString();
}, $classNode->implements);
}
}