mirror of https://github.com/rectorphp/rector.git
194 lines
8.0 KiB
PHP
194 lines
8.0 KiB
PHP
<?php
|
|
|
|
declare (strict_types=1);
|
|
namespace Rector\Core\PhpParser\NodeFinder;
|
|
|
|
use PhpParser\Node\Expr;
|
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
|
use PhpParser\Node\Expr\Assign;
|
|
use PhpParser\Node\Expr\PropertyFetch;
|
|
use PhpParser\Node\Expr\StaticPropertyFetch;
|
|
use PhpParser\Node\Param;
|
|
use PhpParser\Node\Stmt;
|
|
use PhpParser\Node\Stmt\Class_;
|
|
use PhpParser\Node\Stmt\Property;
|
|
use PhpParser\Node\Stmt\Trait_;
|
|
use PHPStan\Type\TypeWithClassName;
|
|
use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
|
|
use Rector\Core\PhpParser\AstResolver;
|
|
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
|
use Rector\Core\Reflection\ReflectionResolver;
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
|
use Rector\NodeTypeResolver\NodeTypeResolver;
|
|
final class PropertyFetchFinder
|
|
{
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
|
|
*/
|
|
private $betterNodeFinder;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\NodeNameResolver\NodeNameResolver
|
|
*/
|
|
private $nodeNameResolver;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Core\Reflection\ReflectionResolver
|
|
*/
|
|
private $reflectionResolver;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Core\PhpParser\AstResolver
|
|
*/
|
|
private $astResolver;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\NodeTypeResolver\NodeTypeResolver
|
|
*/
|
|
private $nodeTypeResolver;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer
|
|
*/
|
|
private $propertyFetchAnalyzer;
|
|
public function __construct(BetterNodeFinder $betterNodeFinder, NodeNameResolver $nodeNameResolver, ReflectionResolver $reflectionResolver, AstResolver $astResolver, NodeTypeResolver $nodeTypeResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer)
|
|
{
|
|
$this->betterNodeFinder = $betterNodeFinder;
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
|
$this->reflectionResolver = $reflectionResolver;
|
|
$this->astResolver = $astResolver;
|
|
$this->nodeTypeResolver = $nodeTypeResolver;
|
|
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
|
|
}
|
|
/**
|
|
* @return array<PropertyFetch|StaticPropertyFetch>
|
|
* @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Param $propertyOrPromotedParam
|
|
*/
|
|
public function findPrivatePropertyFetches(Class_ $class, $propertyOrPromotedParam) : array
|
|
{
|
|
$propertyName = $this->resolvePropertyName($propertyOrPromotedParam);
|
|
if ($propertyName === null) {
|
|
return [];
|
|
}
|
|
$classReflection = $this->reflectionResolver->resolveClassAndAnonymousClass($class);
|
|
$nodes = [$class];
|
|
$nodesTrait = $this->astResolver->parseClassReflectionTraits($classReflection);
|
|
$hasTrait = $nodesTrait !== [];
|
|
$nodes = \array_merge($nodes, $nodesTrait);
|
|
return $this->findPropertyFetchesInClassLike($class, $nodes, $propertyName, $hasTrait);
|
|
}
|
|
/**
|
|
* @return PropertyFetch[]|StaticPropertyFetch[]
|
|
*/
|
|
public function findLocalPropertyFetchesByName(Class_ $class, string $paramName) : array
|
|
{
|
|
/** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */
|
|
$propertyFetches = $this->betterNodeFinder->findInstancesOf($class, [PropertyFetch::class, StaticPropertyFetch::class]);
|
|
$foundPropertyFetches = [];
|
|
foreach ($propertyFetches as $propertyFetch) {
|
|
if ($this->propertyFetchAnalyzer->isLocalPropertyFetchName($propertyFetch, $paramName)) {
|
|
$foundPropertyFetches[] = $propertyFetch;
|
|
}
|
|
}
|
|
return $foundPropertyFetches;
|
|
}
|
|
/**
|
|
* @return ArrayDimFetch[]
|
|
*/
|
|
public function findLocalPropertyArrayDimFetchesAssignsByName(Class_ $class, Property $property) : array
|
|
{
|
|
$propertyName = $this->nodeNameResolver->getName($property);
|
|
/** @var Assign[] $assigns */
|
|
$assigns = $this->betterNodeFinder->findInstanceOf($class, Assign::class);
|
|
$propertyArrayDimFetches = [];
|
|
foreach ($assigns as $assign) {
|
|
if (!$assign->var instanceof ArrayDimFetch) {
|
|
continue;
|
|
}
|
|
$dimFetchVar = $assign->var;
|
|
if (!$dimFetchVar->var instanceof PropertyFetch && !$dimFetchVar->var instanceof StaticPropertyFetch) {
|
|
continue;
|
|
}
|
|
if (!$this->propertyFetchAnalyzer->isLocalPropertyFetchName($dimFetchVar->var, $propertyName)) {
|
|
continue;
|
|
}
|
|
$propertyArrayDimFetches[] = $dimFetchVar;
|
|
}
|
|
return $propertyArrayDimFetches;
|
|
}
|
|
public function isLocalPropertyFetchByName(Expr $expr, string $propertyName) : bool
|
|
{
|
|
if (!$expr instanceof PropertyFetch) {
|
|
return \false;
|
|
}
|
|
if (!$this->nodeNameResolver->isName($expr->name, $propertyName)) {
|
|
return \false;
|
|
}
|
|
return $this->nodeNameResolver->isName($expr->var, 'this');
|
|
}
|
|
/**
|
|
* @param Stmt[] $stmts
|
|
* @return PropertyFetch[]|StaticPropertyFetch[]
|
|
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Trait_ $class
|
|
*/
|
|
private function findPropertyFetchesInClassLike($class, array $stmts, string $propertyName, bool $hasTrait) : array
|
|
{
|
|
/** @var PropertyFetch[] $propertyFetches */
|
|
$propertyFetches = $this->betterNodeFinder->findInstanceOf($stmts, PropertyFetch::class);
|
|
/** @var PropertyFetch[] $matchingPropertyFetches */
|
|
$matchingPropertyFetches = \array_filter($propertyFetches, function (PropertyFetch $propertyFetch) use($propertyName, $class, $hasTrait) : bool {
|
|
if ($this->isInAnonymous($propertyFetch, $class, $hasTrait)) {
|
|
return \false;
|
|
}
|
|
return $this->isNamePropertyNameEquals($propertyFetch, $propertyName, $class);
|
|
});
|
|
/** @var StaticPropertyFetch[] $staticPropertyFetches */
|
|
$staticPropertyFetches = $this->betterNodeFinder->findInstanceOf($stmts, StaticPropertyFetch::class);
|
|
/** @var StaticPropertyFetch[] $matchingStaticPropertyFetches */
|
|
$matchingStaticPropertyFetches = \array_filter($staticPropertyFetches, function (StaticPropertyFetch $staticPropertyFetch) use($propertyName) : bool {
|
|
return $this->nodeNameResolver->isName($staticPropertyFetch->name, $propertyName);
|
|
});
|
|
return \array_merge($matchingPropertyFetches, $matchingStaticPropertyFetches);
|
|
}
|
|
/**
|
|
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Trait_ $class
|
|
*/
|
|
private function isInAnonymous(PropertyFetch $propertyFetch, $class, bool $hasTrait) : bool
|
|
{
|
|
$parentNode = $this->betterNodeFinder->findParentType($propertyFetch, Class_::class);
|
|
if (!$parentNode instanceof Class_) {
|
|
return \false;
|
|
}
|
|
return $parentNode !== $class && !$hasTrait;
|
|
}
|
|
/**
|
|
* @param \PhpParser\Node\Stmt\Class_|\PhpParser\Node\Stmt\Trait_ $class
|
|
*/
|
|
private function isNamePropertyNameEquals(PropertyFetch $propertyFetch, string $propertyName, $class) : bool
|
|
{
|
|
// early check if property fetch name is not equals with property name
|
|
// so next check is check var name and var type only
|
|
if (!$this->isLocalPropertyFetchByName($propertyFetch, $propertyName)) {
|
|
return \false;
|
|
}
|
|
$propertyFetchVarType = $this->nodeTypeResolver->getType($propertyFetch->var);
|
|
if (!$propertyFetchVarType instanceof TypeWithClassName) {
|
|
return \false;
|
|
}
|
|
$propertyFetchVarTypeClassName = $propertyFetchVarType->getClassName();
|
|
$classLikeName = $this->nodeNameResolver->getName($class);
|
|
return $propertyFetchVarTypeClassName === $classLikeName;
|
|
}
|
|
/**
|
|
* @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Param $propertyOrPromotedParam
|
|
*/
|
|
private function resolvePropertyName($propertyOrPromotedParam) : ?string
|
|
{
|
|
if ($propertyOrPromotedParam instanceof Property) {
|
|
return $this->nodeNameResolver->getName($propertyOrPromotedParam->props[0]);
|
|
}
|
|
return $this->nodeNameResolver->getName($propertyOrPromotedParam->var);
|
|
}
|
|
}
|