rector/src/PhpParser/Node/BetterNodeFinder.php

290 lines
7.2 KiB
PHP
Raw Normal View History

2019-10-13 05:59:52 +00:00
<?php
declare(strict_types=1);
namespace Rector\Core\PhpParser\Node;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
2019-01-14 18:21:23 +00:00
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
2018-10-09 10:18:42 +00:00
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeFinder;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
2019-09-03 09:11:45 +00:00
/**
* @see \Rector\Core\Tests\PhpParser\Node\BetterNodeFinder\BetterNodeFinderTest
2019-09-03 09:11:45 +00:00
*/
final class BetterNodeFinder
{
/**
* @var NodeFinder
*/
private $nodeFinder;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(NodeFinder $nodeFinder, NodeNameResolver $nodeNameResolver)
{
$this->nodeFinder = $nodeFinder;
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @param string|string[] $type
*/
public function findFirstParentInstanceOf(Node $node, $type): ?Node
{
if (! is_array($type)) {
$type = [$type];
}
/** @var Node|null $parentNode */
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode === null) {
return null;
}
do {
if ($this->isTypes($parentNode, $type)) {
return $parentNode;
}
if ($parentNode === null) {
return null;
}
} while ($parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE));
return null;
}
public function findFirstAncestorInstanceOf(Node $node, string $type): ?Node
{
$currentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
while ($currentNode !== null) {
if ($currentNode instanceof $type) {
return $currentNode;
}
$currentNode = $currentNode->getAttribute(AttributeKey::PARENT_NODE);
}
2017-10-26 19:06:11 +00:00
return null;
}
/**
* @param string[] $types
*/
public function findFirstAncestorInstancesOf(Node $node, array $types): ?Node
{
$currentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
while ($currentNode !== null) {
foreach ($types as $type) {
if (is_a($currentNode, $type, true)) {
return $currentNode;
}
}
$currentNode = $currentNode->getAttribute(AttributeKey::PARENT_NODE);
}
return null;
}
/**
2019-01-14 18:21:23 +00:00
* @param Node|Node[]|Stmt[] $nodes
* @return Node[]
*/
public function findInstanceOf($nodes, string $type): array
{
return $this->nodeFinder->findInstanceOf($nodes, $type);
}
/**
* @param Node|Node[] $nodes
*/
public function findFirstInstanceOf($nodes, string $type): ?Node
{
return $this->nodeFinder->findFirstInstanceOf($nodes, $type);
}
/**
* @param Node|Node[] $nodes
*/
public function hasInstanceOfName($nodes, string $type, string $name): bool
2020-07-19 13:39:13 +00:00
{
return (bool) $this->findInstanceOfName($nodes, $type, $name);
}
/**
* @param Node|Node[] $nodes
*/
2020-07-19 15:20:00 +00:00
public function hasVariableOfName($nodes, string $name): bool
2020-07-19 13:39:13 +00:00
{
2020-07-19 15:20:00 +00:00
return (bool) $this->findVariableOfName($nodes, $name);
2020-07-19 13:39:13 +00:00
}
/**
* @param Node|Node[] $nodes
*/
2020-07-19 15:20:00 +00:00
public function findVariableOfName($nodes, string $name): ?Node
{
2020-07-19 15:20:00 +00:00
return $this->findInstanceOfName($nodes, Variable::class, $name);
}
/**
* @param Node|Node[] $nodes
* @param string[] $types
*/
public function hasInstancesOf($nodes, array $types): bool
{
foreach ($types as $type) {
if ($this->nodeFinder->findFirstInstanceOf($nodes, $type) === null) {
continue;
}
return true;
}
return false;
}
/**
* @param Node|Node[] $nodes
*/
public function findLastInstanceOf($nodes, string $type): ?Node
{
$foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type);
2019-02-17 14:12:47 +00:00
if ($foundInstances === []) {
return null;
}
return array_pop($foundInstances);
}
/**
* @param Node|Node[] $nodes
* @return Node[]
*/
public function find($nodes, callable $filter): array
{
return $this->nodeFinder->find($nodes, $filter);
}
2017-11-01 21:37:57 +00:00
/**
* Excludes anonymous classes!
*
* @param Node[] $nodes
* @return ClassLike[]
*/
public function findClassLikes(array $nodes): array
{
return $this->find($nodes, function (Node $node): bool {
if (! $node instanceof ClassLike) {
return false;
}
2019-08-18 12:15:39 +00:00
// skip anonymous classes
2019-10-30 14:38:30 +00:00
return ! ($node instanceof Class_ && $node->isAnonymous());
});
}
2020-07-05 11:07:20 +00:00
/**
* @param Node[] $nodes
*/
public function findFirstNonAnonymousClass(array $nodes): ?Class_
{
return $this->findFirst($nodes, function (Node $node): bool {
if (! $node instanceof ClassLike) {
return false;
}
// skip anonymous classes
return ! ($node instanceof Class_ && $node->isAnonymous());
});
}
2017-11-01 21:37:57 +00:00
/**
* @param Node|Node[] $nodes
*/
public function findFirst($nodes, callable $filter): ?Node
{
return $this->nodeFinder->findFirst($nodes, $filter);
}
2018-10-09 10:18:42 +00:00
public function findFirstPrevious(Node $node, callable $filter): ?Node
{
$node = $node instanceof Expression ? $node : $node->getAttribute(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;
}
// move to next expression
2019-10-27 17:40:59 +00:00
$previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_STATEMENT);
if ($previousStatement === null) {
2018-10-09 10:18:42 +00:00
return null;
}
2019-10-27 17:40:59 +00:00
return $this->findFirstPrevious($previousStatement, $filter);
2018-10-09 10:18:42 +00:00
}
2020-03-23 16:13:04 +00:00
/**
* @param class-string[] $types
*/
public function findFirstPreviousOfTypes(Node $mainNode, array $types): ?Node
{
return $this->findFirstPrevious($mainNode, function (Node $node) use ($types) {
foreach ($types as $type) {
if (! is_a($node, $type, true)) {
continue;
}
return true;
}
return false;
});
}
2020-07-19 15:20:00 +00:00
/**
* @param Node|Node[] $nodes
*/
private function findInstanceOfName($nodes, string $type, string $name): ?Node
{
$foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type);
foreach ($foundInstances as $foundInstance) {
if ($this->nodeNameResolver->isName($foundInstance, $name)) {
return $foundInstance;
}
}
return null;
}
/**
* @param string[] $types
*/
private function isTypes(Node $node, array $types): bool
{
foreach ($types as $type) {
if (is_a($node, $type, true)) {
return true;
}
}
return false;
}
}