mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-05 02:40:50 +00:00
[NodeTypeResolver] decouple ConstructorPropertyTypesExtractor
This commit is contained in:
parent
58fef98d4e
commit
b6345de749
|
@ -45,10 +45,14 @@ final class ClassLikeTypeResolver extends NodeVisitorAbstract
|
|||
{
|
||||
if ($node instanceof ClassLike) {
|
||||
$this->typeContext->enterClass($node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof FunctionLike) {
|
||||
$this->typeContext->enterFunction($node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Variable) {
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use ReflectionClass;
|
||||
use Rector\NodeTypeResolver\TypesExtractor\ConstructorPropertyTypesExtractor;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
|
@ -28,10 +25,20 @@ final class TypeContext
|
|||
private $classProperties = [];
|
||||
|
||||
/**
|
||||
* @var ClassLike|nnull
|
||||
* @var ClassLike|null
|
||||
*/
|
||||
private $classLikeNode;
|
||||
|
||||
/**
|
||||
* @var ConstructorPropertyTypesExtractor
|
||||
*/
|
||||
private $constructorPropertyTypesExtractor;
|
||||
|
||||
public function __construct(ConstructorPropertyTypesExtractor $constructorPropertyTypesExtractor)
|
||||
{
|
||||
$this->constructorPropertyTypesExtractor = $constructorPropertyTypesExtractor;
|
||||
}
|
||||
|
||||
public function startFile(): void
|
||||
{
|
||||
$this->types = [];
|
||||
|
@ -50,7 +57,7 @@ final class TypeContext
|
|||
$this->classLikeNode = $classLikeNode;
|
||||
|
||||
if ($classLikeNode instanceof Class_) {
|
||||
$this->classProperties = $this->prepareConstructorTypesFromClass($classLikeNode);
|
||||
$this->classProperties = $this-> constructorPropertyTypesExtractor->extractFromClassNode($classLikeNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,76 +107,4 @@ final class TypeContext
|
|||
|
||||
return new ReflectionFunction((string) $functionLikeNode->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function prepareConstructorTypesFromClass(Class_ $classNode): array
|
||||
{
|
||||
$constructorParametersWithTypes = $this->getConstructorParametersWithTypes($classNode);
|
||||
if (! count($constructorParametersWithTypes)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$propertiesWithTypes = [];
|
||||
foreach ($classNode->stmts as $inClassNode) {
|
||||
if (! $inClassNode instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((string) $inClassNode->name !== '__construct') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($inClassNode->stmts as $inConstructorNode) {
|
||||
if (! $inConstructorNode->expr instanceof Assign) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $inConstructorNode->expr->var instanceof PropertyFetch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($inConstructorNode->expr->var->var->name !== 'this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PropertyFetch $propertyFetchNode */
|
||||
$propertyFetchNode = $inConstructorNode->expr->var;
|
||||
$propertyName = (string) $propertyFetchNode->name;
|
||||
$propertyType = $constructorParametersWithTypes[$propertyName] ?? null;
|
||||
|
||||
if ($propertyName && $propertyType) {
|
||||
$propertiesWithTypes[$propertyName] = $propertyType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $propertiesWithTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getConstructorParametersWithTypes(Class_ $classNode): array
|
||||
{
|
||||
$className = $classNode->namespacedName->toString();
|
||||
if (! class_exists($className)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$constructorMethod = (new ReflectionClass($className))->getConstructor();
|
||||
$parametersWithTypes = [];
|
||||
|
||||
if ($constructorMethod) {
|
||||
foreach ($constructorMethod->getParameters() as $parameterReflection) {
|
||||
$parameterName = $parameterReflection->getName();
|
||||
$parameterType = (string) $parameterReflection->getType();
|
||||
|
||||
$parametersWithTypes[$parameterName] = $parameterType;
|
||||
}
|
||||
}
|
||||
|
||||
return $parametersWithTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\TypesExtractor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
|
||||
final class ConstructorPropertyTypesExtractor
|
||||
{
|
||||
/**
|
||||
* @return string[] { propertyName => propertyType }
|
||||
*/
|
||||
public function extractFromClassNode(Class_ $classNode): array
|
||||
{
|
||||
$constructorParametersWithTypes = $this->getConstructorParametersWithTypes($classNode);
|
||||
if (! count($constructorParametersWithTypes)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$propertiesWithTypes = [];
|
||||
foreach ($classNode->stmts as $inClassNode) {
|
||||
if (! $this->isContructorMethodNode($inClassNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($inClassNode->stmts as $inConstructorNode) {
|
||||
if (! $this->isAssignThisNode($inConstructorNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PropertyFetch $propertyFetchNode */
|
||||
$propertyFetchNode = $inConstructorNode->expr->var;
|
||||
$propertyName = (string) $propertyFetchNode->name;
|
||||
$propertyType = $constructorParametersWithTypes[$propertyName] ?? null;
|
||||
|
||||
if ($propertyName && $propertyType) {
|
||||
$propertiesWithTypes[$propertyName] = $propertyType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $propertiesWithTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] { parameterName => parameterType }
|
||||
*/
|
||||
private function getConstructorParametersWithTypes(Class_ $classNode): array
|
||||
{
|
||||
$className = $classNode->namespacedName->toString();
|
||||
if (! class_exists($className)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$constructorMethod = (new \ReflectionClass($className))->getConstructor();
|
||||
$parametersWithTypes = [];
|
||||
|
||||
if ($constructorMethod) {
|
||||
foreach ($constructorMethod->getParameters() as $parameterReflection) {
|
||||
$parameterName = $parameterReflection->getName();
|
||||
$parameterType = (string) $parameterReflection->getType();
|
||||
|
||||
$parametersWithTypes[$parameterName] = $parameterType;
|
||||
}
|
||||
}
|
||||
|
||||
return $parametersWithTypes;
|
||||
}
|
||||
|
||||
private function isContructorMethodNode(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof ClassMethod) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (string) $node->name === '__construct';
|
||||
}
|
||||
|
||||
private function isAssignThisNode(Node $node): bool
|
||||
{
|
||||
if (! $node->expr instanceof Assign) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->expr->var instanceof PropertyFetch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $node->expr->var->var->name === 'this';
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ final class ClassLikeTypeResolverTest extends AbstractContainerAwareTestCase
|
|||
$propertyFetchNode = $nodes[1]->stmts[1]->stmts[2]->stmts[0]->expr;
|
||||
$this->assertSame(Html::class, $propertyFetchNode->getAttribute('type'));
|
||||
|
||||
// @todo
|
||||
// @todo test asl well
|
||||
//$propertyNode = $nodes[1]->stmts[1]->stmts[0];
|
||||
// $constructorVariableNode = $nodes[1]->stmts[1]->stmts[1]->params[0]->var;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user