2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2020-02-06 21:48:18 +00:00
|
|
|
namespace Rector\Core\PhpParser\Node;
|
2017-10-26 16:43:43 +00:00
|
|
|
|
|
|
|
use PhpParser\Node;
|
2020-07-25 20:02:44 +00:00
|
|
|
use PhpParser\Node\Expr;
|
|
|
|
use PhpParser\Node\Expr\Assign;
|
2021-11-06 14:57:23 +00:00
|
|
|
use PhpParser\Node\Expr\Closure;
|
2021-07-15 03:40:51 +00:00
|
|
|
use PhpParser\Node\Expr\PropertyFetch;
|
|
|
|
use PhpParser\Node\Expr\StaticPropertyFetch;
|
2020-07-22 16:43:23 +00:00
|
|
|
use PhpParser\Node\Expr\Variable;
|
2021-03-24 22:36:56 +00:00
|
|
|
use PhpParser\Node\FunctionLike;
|
2019-01-14 18:21:23 +00:00
|
|
|
use PhpParser\Node\Stmt;
|
2019-05-23 17:32:46 +00:00
|
|
|
use PhpParser\Node\Stmt\Class_;
|
|
|
|
use PhpParser\Node\Stmt\ClassLike;
|
2021-11-06 14:57:23 +00:00
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
2018-10-09 10:18:42 +00:00
|
|
|
use PhpParser\Node\Stmt\Expression;
|
2021-11-06 14:57:23 +00:00
|
|
|
use PhpParser\Node\Stmt\Function_;
|
|
|
|
use PhpParser\Node\Stmt\Namespace_;
|
2021-07-15 03:40:51 +00:00
|
|
|
use PhpParser\Node\Stmt\Property;
|
2021-04-15 15:56:21 +00:00
|
|
|
use PhpParser\Node\Stmt\Return_;
|
2017-10-26 16:43:43 +00:00
|
|
|
use PhpParser\NodeFinder;
|
2021-04-18 13:01:22 +00:00
|
|
|
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
2021-02-19 12:01:23 +00:00
|
|
|
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
2020-07-19 11:11:20 +00:00
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
2019-04-13 09:20:27 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2022-01-07 00:57:38 +00:00
|
|
|
use RectorPrefix20220107\Symplify\PackageBuilder\Php\TypeChecker;
|
|
|
|
use RectorPrefix20220107\Webmozart\Assert\Assert;
|
2019-09-03 09:11:45 +00:00
|
|
|
/**
|
2020-02-06 21:48:18 +00:00
|
|
|
* @see \Rector\Core\Tests\PhpParser\Node\BetterNodeFinder\BetterNodeFinderTest
|
2019-09-03 09:11:45 +00:00
|
|
|
*/
|
2017-10-26 16:43:43 +00:00
|
|
|
final class BetterNodeFinder
|
|
|
|
{
|
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-08-23 00:20:32 +00:00
|
|
|
* @var \PhpParser\NodeFinder
|
2017-10-26 16:43:43 +00:00
|
|
|
*/
|
|
|
|
private $nodeFinder;
|
2020-07-19 11:11:20 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\NodeNameResolver\NodeNameResolver
|
2020-07-19 11:11:20 +00:00
|
|
|
*/
|
|
|
|
private $nodeNameResolver;
|
2020-07-25 20:02:44 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-08-23 00:20:32 +00:00
|
|
|
* @var \Symplify\PackageBuilder\Php\TypeChecker
|
2020-07-25 20:02:44 +00:00
|
|
|
*/
|
2021-02-19 12:01:23 +00:00
|
|
|
private $typeChecker;
|
2021-01-19 19:45:30 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\PhpParser\Comparing\NodeComparator
|
2021-01-19 19:45:30 +00:00
|
|
|
*/
|
2021-02-19 12:01:23 +00:00
|
|
|
private $nodeComparator;
|
2021-04-18 13:01:22 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\NodeAnalyzer\ClassAnalyzer
|
2021-04-18 13:01:22 +00:00
|
|
|
*/
|
|
|
|
private $classAnalyzer;
|
2022-01-07 00:57:38 +00:00
|
|
|
public function __construct(\PhpParser\NodeFinder $nodeFinder, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \RectorPrefix20220107\Symplify\PackageBuilder\Php\TypeChecker $typeChecker, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, \Rector\Core\NodeAnalyzer\ClassAnalyzer $classAnalyzer)
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2017-10-26 16:43:43 +00:00
|
|
|
$this->nodeFinder = $nodeFinder;
|
2020-07-19 11:11:20 +00:00
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
2021-01-19 19:45:30 +00:00
|
|
|
$this->typeChecker = $typeChecker;
|
2021-02-19 12:01:23 +00:00
|
|
|
$this->nodeComparator = $nodeComparator;
|
2021-04-18 13:01:22 +00:00
|
|
|
$this->classAnalyzer = $classAnalyzer;
|
2021-11-06 14:57:23 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @template T of \PhpParser\Node
|
|
|
|
* @param array<class-string<T>> $types
|
|
|
|
* @return T|null
|
|
|
|
*/
|
|
|
|
public function findParentByTypes(\PhpParser\Node $currentNode, array $types) : ?\PhpParser\Node
|
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::allIsAOf($types, \PhpParser\Node::class);
|
2021-11-06 14:57:23 +00:00
|
|
|
while ($currentNode = $currentNode->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE)) {
|
|
|
|
if (!$currentNode instanceof \PhpParser\Node) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
foreach ($types as $type) {
|
|
|
|
if (\is_a($currentNode, $type, \true)) {
|
|
|
|
return $currentNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2019-03-31 18:13:46 +00:00
|
|
|
}
|
2019-03-31 19:30:26 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
2021-01-08 22:30:33 +00:00
|
|
|
* @param class-string<T> $type
|
2021-07-17 17:24:05 +00:00
|
|
|
* @return T|null
|
2019-03-31 19:30:26 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findParentType(\PhpParser\Node $node, string $type) : ?\PhpParser\Node
|
2021-01-08 22:30:33 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::isAOf($type, \PhpParser\Node::class);
|
2021-05-10 22:23:08 +00:00
|
|
|
$parent = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
|
|
|
if (!$parent instanceof \PhpParser\Node) {
|
2021-01-08 22:30:33 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
do {
|
2021-05-09 20:15:43 +00:00
|
|
|
if (\is_a($parent, $type, \true)) {
|
2021-01-08 22:30:33 +00:00
|
|
|
return $parent;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$parent instanceof \PhpParser\Node) {
|
2021-01-08 22:30:33 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
} while ($parent = $parent->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE));
|
2021-01-08 22:30:33 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-05-02 10:46:55 +00:00
|
|
|
/**
|
|
|
|
* @template T of Node
|
|
|
|
* @param array<class-string<T>> $types
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2021-05-02 10:46:55 +00:00
|
|
|
* @return T[]
|
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function findInstancesOf($nodes, array $types) : array
|
2021-05-02 10:46:55 +00:00
|
|
|
{
|
|
|
|
$foundInstances = [];
|
|
|
|
foreach ($types as $type) {
|
|
|
|
$currentFoundInstances = $this->findInstanceOf($nodes, $type);
|
2021-05-09 20:15:43 +00:00
|
|
|
$foundInstances = \array_merge($foundInstances, $currentFoundInstances);
|
2021-05-02 10:46:55 +00:00
|
|
|
}
|
|
|
|
return $foundInstances;
|
|
|
|
}
|
2019-07-10 07:23:37 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
2021-01-08 22:30:33 +00:00
|
|
|
* @param class-string<T> $type
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2021-01-08 22:30:33 +00:00
|
|
|
* @return T[]
|
2017-10-26 16:43:43 +00:00
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function findInstanceOf($nodes, string $type) : array
|
2017-10-26 16:43:43 +00:00
|
|
|
{
|
|
|
|
return $this->nodeFinder->findInstanceOf($nodes, $type);
|
|
|
|
}
|
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
2021-01-08 22:30:33 +00:00
|
|
|
* @param class-string<T> $type
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2017-10-26 16:43:43 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstInstanceOf($nodes, string $type) : ?\PhpParser\Node
|
2017-10-26 16:43:43 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::isAOf($type, \PhpParser\Node::class);
|
2017-10-26 16:43:43 +00:00
|
|
|
return $this->nodeFinder->findFirstInstanceOf($nodes, $type);
|
|
|
|
}
|
2020-07-19 11:11:20 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @param class-string<Node> $type
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2020-07-19 11:11:20 +00:00
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function hasInstanceOfName($nodes, string $type, string $name) : bool
|
2020-07-19 13:39:13 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::isAOf($type, \PhpParser\Node::class);
|
2020-07-19 13:39:13 +00:00
|
|
|
return (bool) $this->findInstanceOfName($nodes, $type, $name);
|
|
|
|
}
|
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2020-07-19 13:39:13 +00:00
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function hasVariableOfName($nodes, string $name) : bool
|
2020-07-19 13:39:13 +00:00
|
|
|
{
|
2021-11-06 14:57:23 +00:00
|
|
|
return $this->findVariableOfName($nodes, $name) instanceof \PhpParser\Node;
|
2020-07-19 13:39:13 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2021-01-08 22:30:33 +00:00
|
|
|
* @return Variable|null
|
2020-07-19 13:39:13 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findVariableOfName($nodes, string $name) : ?\PhpParser\Node
|
2020-07-19 11:11:20 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
return $this->findInstanceOfName($nodes, \PhpParser\Node\Expr\Variable::class, $name);
|
2020-07-19 11:11:20 +00:00
|
|
|
}
|
2020-03-30 19:08:12 +00:00
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2021-03-18 11:21:20 +00:00
|
|
|
* @param array<class-string<Node>> $types
|
2020-03-30 19:08:12 +00:00
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function hasInstancesOf($nodes, array $types) : bool
|
2020-03-30 19:08:12 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::allIsAOf($types, \PhpParser\Node::class);
|
2020-03-30 19:08:12 +00:00
|
|
|
foreach ($types as $type) {
|
2021-03-18 11:21:20 +00:00
|
|
|
$foundNode = $this->nodeFinder->findFirstInstanceOf($nodes, $type);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$foundNode instanceof \PhpParser\Node) {
|
2020-03-30 19:08:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2020-03-30 19:08:12 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-03-30 19:08:12 +00:00
|
|
|
}
|
2018-01-15 22:14:44 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
2021-01-08 22:30:33 +00:00
|
|
|
* @param class-string<T> $type
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2018-01-15 22:14:44 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findLastInstanceOf($nodes, string $type) : ?\PhpParser\Node
|
2018-01-15 22:14:44 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::isAOf($type, \PhpParser\Node::class);
|
2018-01-15 22:14:44 +00:00
|
|
|
$foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type);
|
2019-02-17 14:12:47 +00:00
|
|
|
if ($foundInstances === []) {
|
2018-01-15 22:14:44 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
\end($foundInstances);
|
|
|
|
$lastItemKey = \key($foundInstances);
|
2021-01-08 22:30:33 +00:00
|
|
|
return $foundInstances[$lastItemKey];
|
2018-01-15 22:14:44 +00:00
|
|
|
}
|
2017-10-26 19:20:05 +00:00
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2017-10-26 19:20:05 +00:00
|
|
|
* @return Node[]
|
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
public function find($nodes, callable $filter) : array
|
2017-10-26 19:20:05 +00:00
|
|
|
{
|
|
|
|
return $this->nodeFinder->find($nodes, $filter);
|
|
|
|
}
|
2020-07-05 11:07:20 +00:00
|
|
|
/**
|
|
|
|
* @param Node[] $nodes
|
2021-01-08 22:30:33 +00:00
|
|
|
* @return ClassLike|null
|
2020-07-05 11:07:20 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstNonAnonymousClass(array $nodes) : ?\PhpParser\Node
|
2020-07-05 11:07:20 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
return $this->findFirst($nodes, function (\PhpParser\Node $node) : bool {
|
|
|
|
if (!$node instanceof \PhpParser\Node\Stmt\ClassLike) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-07-05 11:07:20 +00:00
|
|
|
}
|
|
|
|
// skip anonymous classes
|
2021-05-10 22:23:08 +00:00
|
|
|
return !($node instanceof \PhpParser\Node\Stmt\Class_ && $this->classAnalyzer->isAnonymousClass($node));
|
2020-07-05 11:07:20 +00:00
|
|
|
});
|
|
|
|
}
|
2017-11-01 21:37:57 +00:00
|
|
|
/**
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2017-11-01 21:37:57 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirst($nodes, callable $filter) : ?\PhpParser\Node
|
2017-11-01 21:37:57 +00:00
|
|
|
{
|
|
|
|
return $this->nodeFinder->findFirst($nodes, $filter);
|
|
|
|
}
|
2021-11-13 16:23:20 +00:00
|
|
|
/**
|
|
|
|
* @return Assign[]
|
|
|
|
*/
|
|
|
|
public function findClassMethodAssignsToLocalProperty(\PhpParser\Node\Stmt\ClassMethod $classMethod, string $propertyName) : array
|
|
|
|
{
|
2021-12-23 13:23:36 +00:00
|
|
|
return $this->find((array) $classMethod->stmts, function (\PhpParser\Node $node) use($classMethod, $propertyName) : bool {
|
2021-11-13 16:23:20 +00:00
|
|
|
if (!$node instanceof \PhpParser\Node\Expr\Assign) {
|
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
if (!$node->var instanceof \PhpParser\Node\Expr\PropertyFetch) {
|
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
$propertyFetch = $node->var;
|
|
|
|
if (!$this->nodeNameResolver->isName($propertyFetch->var, 'this')) {
|
|
|
|
return \false;
|
|
|
|
}
|
2021-12-23 13:23:36 +00:00
|
|
|
$parentFunctionLike = $this->findParentType($node, \PhpParser\Node\Stmt\ClassMethod::class);
|
|
|
|
if ($parentFunctionLike !== $classMethod) {
|
|
|
|
return \false;
|
|
|
|
}
|
2021-11-13 16:23:20 +00:00
|
|
|
return $this->nodeNameResolver->isName($propertyFetch->name, $propertyName);
|
|
|
|
});
|
|
|
|
}
|
2021-01-08 22:30:33 +00:00
|
|
|
/**
|
|
|
|
* @return Assign|null
|
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findPreviousAssignToExpr(\PhpParser\Node\Expr $expr) : ?\PhpParser\Node
|
2020-07-25 20:02:44 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
return $this->findFirstPrevious($expr, function (\PhpParser\Node $node) use($expr) : bool {
|
|
|
|
if (!$node instanceof \PhpParser\Node\Expr\Assign) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-07-25 20:02:44 +00:00
|
|
|
}
|
2021-02-19 12:01:23 +00:00
|
|
|
return $this->nodeComparator->areNodesEqual($node->var, $expr);
|
2020-07-25 20:02:44 +00:00
|
|
|
});
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstPreviousOfNode(\PhpParser\Node $node, callable $filter) : ?\PhpParser\Node
|
2021-02-14 09:53:04 +00:00
|
|
|
{
|
|
|
|
// move to previous expression
|
2021-05-10 22:23:08 +00:00
|
|
|
$previousStatement = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_NODE);
|
2021-02-14 09:53:04 +00:00
|
|
|
if ($previousStatement !== null) {
|
|
|
|
$foundNode = $this->findFirst([$previousStatement], $filter);
|
|
|
|
// we found what we need
|
|
|
|
if ($foundNode !== null) {
|
|
|
|
return $foundNode;
|
|
|
|
}
|
|
|
|
return $this->findFirstPreviousOfNode($previousStatement, $filter);
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
$parent = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
|
|
|
if ($parent instanceof \PhpParser\Node\FunctionLike) {
|
2021-03-24 22:36:56 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($parent instanceof \PhpParser\Node) {
|
2021-02-14 09:53:04 +00:00
|
|
|
return $this->findFirstPreviousOfNode($parent, $filter);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstPrevious(\PhpParser\Node $node, callable $filter) : ?\PhpParser\Node
|
2018-10-09 10:18:42 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
$node = $node instanceof \PhpParser\Node\Stmt\Expression ? $node : $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::CURRENT_STATEMENT);
|
2019-01-25 00:49:26 +00:00
|
|
|
if ($node === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-10-09 10:18:42 +00:00
|
|
|
$foundNode = $this->findFirst([$node], $filter);
|
|
|
|
// we found what we need
|
2019-02-17 14:12:47 +00:00
|
|
|
if ($foundNode !== null) {
|
2018-10-09 10:18:42 +00:00
|
|
|
return $foundNode;
|
|
|
|
}
|
2020-12-23 14:33:14 +00:00
|
|
|
// move to previous expression
|
2021-05-10 22:23:08 +00:00
|
|
|
$previousStatement = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PREVIOUS_STATEMENT);
|
2020-12-23 14:33:14 +00:00
|
|
|
if ($previousStatement !== null) {
|
|
|
|
return $this->findFirstPrevious($previousStatement, $filter);
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
$parent = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
2020-12-23 14:33:14 +00:00
|
|
|
if ($parent === null) {
|
2018-10-09 10:18:42 +00:00
|
|
|
return null;
|
|
|
|
}
|
2020-12-23 14:33:14 +00:00
|
|
|
return $this->findFirstPrevious($parent, $filter);
|
2018-10-09 10:18:42 +00:00
|
|
|
}
|
2020-03-23 16:13:04 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
|
|
|
* @param array<class-string<T>> $types
|
2020-03-23 16:13:04 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstPreviousOfTypes(\PhpParser\Node $mainNode, array $types) : ?\PhpParser\Node
|
2020-03-23 16:13:04 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
return $this->findFirstPrevious($mainNode, function (\PhpParser\Node $node) use($types) : bool {
|
2021-01-19 19:45:30 +00:00
|
|
|
return $this->typeChecker->isInstanceOf($node, $types);
|
2020-03-23 16:13:04 +00:00
|
|
|
});
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
public function findFirstNext(\PhpParser\Node $node, callable $filter) : ?\PhpParser\Node
|
2021-02-05 14:15:51 +00:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
$next = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::NEXT_NODE);
|
|
|
|
if ($next instanceof \PhpParser\Node) {
|
|
|
|
if ($next instanceof \PhpParser\Node\Stmt\Return_ && $next->expr === null) {
|
2021-04-15 15:56:21 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-02-05 14:15:51 +00:00
|
|
|
$found = $this->findFirst($next, $filter);
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($found instanceof \PhpParser\Node) {
|
2021-02-05 14:15:51 +00:00
|
|
|
return $found;
|
|
|
|
}
|
|
|
|
return $this->findFirstNext($next, $filter);
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
$parent = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
|
|
|
if ($parent instanceof \PhpParser\Node\Stmt\Return_ || $parent instanceof \PhpParser\Node\FunctionLike) {
|
2021-03-25 21:20:10 +00:00
|
|
|
return null;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if ($parent instanceof \PhpParser\Node) {
|
2021-02-05 14:15:51 +00:00
|
|
|
return $this->findFirstNext($parent, $filter);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-07-15 03:40:51 +00:00
|
|
|
/**
|
|
|
|
* @return Expr[]
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param \PhpParser\Node\Expr|\PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Stmt\Property $expr
|
2021-07-15 03:40:51 +00:00
|
|
|
*/
|
|
|
|
public function findSameNamedExprs($expr) : array
|
|
|
|
{
|
|
|
|
// assign of empty string to something
|
2021-11-06 14:57:23 +00:00
|
|
|
$scopeNode = $this->findParentScope($expr);
|
|
|
|
if (!$scopeNode instanceof \PhpParser\Node) {
|
2021-07-15 03:40:51 +00:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
if ($expr instanceof \PhpParser\Node\Expr\Variable) {
|
|
|
|
$exprName = $this->nodeNameResolver->getName($expr);
|
|
|
|
if ($exprName === null) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
$variables = $this->findInstancesOf($scopeNode, [\PhpParser\Node\Expr\Variable::class]);
|
2021-07-31 11:40:13 +00:00
|
|
|
return \array_filter($variables, function (\PhpParser\Node\Expr\Variable $variable) use($exprName) : bool {
|
2021-07-15 03:40:51 +00:00
|
|
|
return $this->nodeNameResolver->isName($variable, $exprName);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if ($expr instanceof \PhpParser\Node\Stmt\Property) {
|
|
|
|
$singleProperty = $expr->props[0];
|
|
|
|
$exprName = $this->nodeNameResolver->getName($singleProperty->name);
|
|
|
|
} elseif ($expr instanceof \PhpParser\Node\Expr\StaticPropertyFetch || $expr instanceof \PhpParser\Node\Expr\PropertyFetch) {
|
|
|
|
$exprName = $this->nodeNameResolver->getName($expr->name);
|
|
|
|
} else {
|
2021-07-17 10:28:15 +00:00
|
|
|
return [];
|
2021-07-15 03:40:51 +00:00
|
|
|
}
|
|
|
|
if ($exprName === null) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
$propertyFetches = $this->findInstancesOf($scopeNode, [\PhpParser\Node\Expr\PropertyFetch::class, \PhpParser\Node\Expr\StaticPropertyFetch::class]);
|
2021-07-31 11:40:13 +00:00
|
|
|
return \array_filter($propertyFetches, function ($propertyFetch) use($exprName) : bool {
|
2021-07-15 03:40:51 +00:00
|
|
|
return $this->nodeNameResolver->isName($propertyFetch->name, $exprName);
|
|
|
|
});
|
|
|
|
}
|
2021-12-06 19:50:11 +00:00
|
|
|
/**
|
|
|
|
* @template T of Node
|
|
|
|
* @param array<class-string<T>>|class-string<T> $types
|
2021-12-09 13:05:29 +00:00
|
|
|
* @param \PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
2021-12-06 19:50:11 +00:00
|
|
|
*/
|
|
|
|
public function hasInstancesOfInFunctionLikeScoped($functionLike, $types) : bool
|
|
|
|
{
|
|
|
|
if (\is_string($types)) {
|
|
|
|
$types = [$types];
|
|
|
|
}
|
|
|
|
foreach ($types as $type) {
|
|
|
|
$foundNode = $this->findFirstInstanceOf((array) $functionLike->stmts, $type);
|
|
|
|
if (!$foundNode instanceof \PhpParser\Node) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-09 13:05:29 +00:00
|
|
|
$parentFunctionLike = $this->findParentByTypes($foundNode, [\PhpParser\Node\Stmt\ClassMethod::class, \PhpParser\Node\Stmt\Function_::class, \PhpParser\Node\Expr\Closure::class]);
|
2021-12-06 19:50:11 +00:00
|
|
|
if ($parentFunctionLike === $functionLike) {
|
|
|
|
return \true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return \false;
|
|
|
|
}
|
2021-12-11 16:16:09 +00:00
|
|
|
/**
|
|
|
|
* @template T of Node
|
|
|
|
* @param array<class-string<T>>|class-string<T> $types
|
|
|
|
* @return T[]
|
|
|
|
* @param \PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
|
|
|
*/
|
|
|
|
public function findInstancesOfInFunctionLikeScoped($functionLike, $types) : array
|
|
|
|
{
|
|
|
|
if (\is_string($types)) {
|
|
|
|
$types = [$types];
|
|
|
|
}
|
|
|
|
/** @var T[] $foundNodes */
|
|
|
|
$foundNodes = [];
|
|
|
|
foreach ($types as $type) {
|
|
|
|
/** @var T[] $nodes */
|
|
|
|
$nodes = $this->findInstanceOf((array) $functionLike->stmts, $type);
|
|
|
|
if ($nodes === []) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
foreach ($nodes as $key => $node) {
|
|
|
|
$parentFunctionLike = $this->findParentByTypes($node, [\PhpParser\Node\Stmt\ClassMethod::class, \PhpParser\Node\Stmt\Function_::class, \PhpParser\Node\Expr\Closure::class]);
|
|
|
|
if ($parentFunctionLike !== $functionLike) {
|
|
|
|
unset($nodes[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($nodes === []) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$foundNodes = \array_merge($foundNodes, $nodes);
|
|
|
|
}
|
|
|
|
return $foundNodes;
|
|
|
|
}
|
2021-12-09 14:35:22 +00:00
|
|
|
/**
|
|
|
|
* @param \PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
|
|
|
*/
|
|
|
|
public function findFirstInFunctionLikeScoped($functionLike, callable $filter) : ?\PhpParser\Node
|
|
|
|
{
|
|
|
|
$foundNode = $this->findFirst((array) $functionLike->stmts, $filter);
|
|
|
|
if (!$foundNode instanceof \PhpParser\Node) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$parentFunctionLike = $this->findParentByTypes($foundNode, [\PhpParser\Node\Stmt\ClassMethod::class, \PhpParser\Node\Stmt\Function_::class, \PhpParser\Node\Expr\Closure::class]);
|
|
|
|
if ($parentFunctionLike !== $functionLike) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return $foundNode;
|
|
|
|
}
|
2019-03-31 19:30:26 +00:00
|
|
|
/**
|
2021-03-18 11:21:20 +00:00
|
|
|
* @template T of Node
|
2021-10-30 14:18:31 +00:00
|
|
|
* @param mixed[]|\PhpParser\Node $nodes
|
2021-01-08 22:30:33 +00:00
|
|
|
* @param class-string<T> $type
|
2019-03-31 19:30:26 +00:00
|
|
|
*/
|
2021-05-10 22:23:08 +00:00
|
|
|
private function findInstanceOfName($nodes, string $type, string $name) : ?\PhpParser\Node
|
2019-03-31 19:30:26 +00:00
|
|
|
{
|
2022-01-07 00:57:38 +00:00
|
|
|
\RectorPrefix20220107\Webmozart\Assert\Assert::isAOf($type, \PhpParser\Node::class);
|
2020-08-05 20:45:36 +00:00
|
|
|
$foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type);
|
|
|
|
foreach ($foundInstances as $foundInstance) {
|
2021-05-09 20:15:43 +00:00
|
|
|
if (!$this->nodeNameResolver->isName($foundInstance, $name)) {
|
2021-01-08 22:30:33 +00:00
|
|
|
continue;
|
2019-03-31 19:30:26 +00:00
|
|
|
}
|
2021-01-08 22:30:33 +00:00
|
|
|
return $foundInstance;
|
2019-03-31 19:30:26 +00:00
|
|
|
}
|
2020-08-05 20:45:36 +00:00
|
|
|
return null;
|
2019-03-31 19:30:26 +00:00
|
|
|
}
|
2021-11-06 14:57:23 +00:00
|
|
|
/**
|
|
|
|
* @return \PhpParser\Node|null
|
|
|
|
*/
|
|
|
|
private function findParentScope(\PhpParser\Node $node)
|
|
|
|
{
|
|
|
|
return $this->findParentByTypes($node, [\PhpParser\Node\Expr\Closure::class, \PhpParser\Node\Stmt\Function_::class, \PhpParser\Node\Stmt\ClassMethod::class, \PhpParser\Node\Stmt\Class_::class, \PhpParser\Node\Stmt\Namespace_::class]);
|
|
|
|
}
|
2017-10-26 16:43:43 +00:00
|
|
|
}
|