nodeFinder = $nodeFinder; $this->nodeNameResolver = $nodeNameResolver; $this->typeChecker = $typeChecker; $this->nodeComparator = $nodeComparator; $this->classAnalyzer = $classAnalyzer; } /** * @template T of Node * @param class-string $type * @return T|null */ public function findParentType(Node $node, string $type): ?Node { Assert::isAOf($type, Node::class); $parent = $node->getAttribute(AttributeKey::PARENT_NODE); if (! $parent instanceof Node) { return null; } do { if (is_a($parent, $type, true)) { return $parent; } if (! $parent instanceof Node) { return null; } } while ($parent = $parent->getAttribute(AttributeKey::PARENT_NODE)); return null; } /** * @template T of \PhpParser\Node * @param array> $types * @return T|null */ public function findParentTypes(Node $node, array $types): ?Node { Assert::allIsAOf($types, Node::class); $parent = $node->getAttribute(AttributeKey::PARENT_NODE); if (! $parent instanceof Node) { return null; } do { foreach ($types as $type) { if (is_a($parent, $type, true)) { return $parent; } } if ($parent === null) { return null; } } while ($parent = $parent->getAttribute(AttributeKey::PARENT_NODE)); return null; } /** * @template T of Node * @param array> $types * @param Node|Node[]|Stmt[] $nodes * @return T[] */ public function findInstancesOf($nodes, array $types): array { $foundInstances = []; foreach ($types as $type) { $currentFoundInstances = $this->findInstanceOf($nodes, $type); $foundInstances = array_merge($foundInstances, $currentFoundInstances); } return $foundInstances; } /** * @template T of Node * @param class-string $type * @param Node|Node[]|Stmt[] $nodes * @return T[] */ public function findInstanceOf($nodes, string $type): array { Assert::isAOf($type, Node::class); return $this->nodeFinder->findInstanceOf($nodes, $type); } /** * @template T of Node * @param class-string $type * @param Node|Node[] $nodes * @return T|null */ public function findFirstInstanceOf($nodes, string $type): ?Node { Assert::isAOf($type, Node::class); return $this->nodeFinder->findFirstInstanceOf($nodes, $type); } /** * @param class-string $type * @param Node|Node[] $nodes */ public function hasInstanceOfName($nodes, string $type, string $name): bool { Assert::isAOf($type, Node::class); return (bool) $this->findInstanceOfName($nodes, $type, $name); } /** * @param Node|Node[] $nodes */ public function hasVariableOfName($nodes, string $name): bool { return (bool) $this->findVariableOfName($nodes, $name); } /** * @param Node|Node[] $nodes * @return Variable|null */ public function findVariableOfName($nodes, string $name): ?Node { return $this->findInstanceOfName($nodes, Variable::class, $name); } /** * @param Node|Node[] $nodes * @param array> $types */ public function hasInstancesOf($nodes, array $types): bool { Assert::allIsAOf($types, Node::class); foreach ($types as $type) { $foundNode = $this->nodeFinder->findFirstInstanceOf($nodes, $type); if (! $foundNode instanceof Node) { continue; } return true; } return false; } /** * @template T of Node * @param class-string $type * @param Node|Node[] $nodes * @return T|null */ public function findLastInstanceOf($nodes, string $type): ?Node { Assert::isAOf($type, Node::class); $foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type); if ($foundInstances === []) { return null; } $lastItemKey = array_key_last($foundInstances); return $foundInstances[$lastItemKey]; } /** * @param Node|Node[] $nodes * @return Node[] */ public function find($nodes, callable $filter): array { return $this->nodeFinder->find($nodes, $filter); } /** * Excludes anonymous classes! * * @param Node[]|Node $nodes * @return ClassLike[] */ public function findClassLikes($nodes): array { return $this->find($nodes, function (Node $node): bool { if (! $node instanceof ClassLike) { return false; } // skip anonymous classes return ! ($node instanceof Class_ && $this->classAnalyzer->isAnonymousClass($node)); }); } /** * @param Node[] $nodes * @return ClassLike|null */ public function findFirstNonAnonymousClass(array $nodes): ?Node { return $this->findFirst($nodes, function (Node $node): bool { if (! $node instanceof ClassLike) { return false; } // skip anonymous classes return ! ($node instanceof Class_ && $this->classAnalyzer->isAnonymousClass($node)); }); } /** * @param Node|Node[] $nodes */ public function findFirst($nodes, callable $filter): ?Node { return $this->nodeFinder->findFirst($nodes, $filter); } /** * @return Assign|null */ public function findPreviousAssignToExpr(Expr $expr): ?Node { return $this->findFirstPrevious($expr, function (Node $node) use ($expr): bool { if (! $node instanceof Assign) { return false; } return $this->nodeComparator->areNodesEqual($node->var, $expr); }); } public function findFirstPreviousOfNode(Node $node, callable $filter): ?Node { // move to previous expression $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_NODE); if ($previousStatement !== null) { $foundNode = $this->findFirst([$previousStatement], $filter); // we found what we need if ($foundNode !== null) { return $foundNode; } return $this->findFirstPreviousOfNode($previousStatement, $filter); } $parent = $node->getAttribute(AttributeKey::PARENT_NODE); if ($parent instanceof FunctionLike) { return null; } if ($parent instanceof Node) { return $this->findFirstPreviousOfNode($parent, $filter); } return null; } public function findFirstPrevious(Node $node, callable $filter): ?Node { $node = $node instanceof Expression ? $node : $node->getAttribute(AttributeKey::CURRENT_STATEMENT); if ($node === null) { return null; } $foundNode = $this->findFirst([$node], $filter); // we found what we need if ($foundNode !== null) { return $foundNode; } // move to previous expression $previousStatement = $node->getAttribute(AttributeKey::PREVIOUS_STATEMENT); if ($previousStatement !== null) { return $this->findFirstPrevious($previousStatement, $filter); } $parent = $node->getAttribute(AttributeKey::PARENT_NODE); if ($parent === null) { return null; } return $this->findFirstPrevious($parent, $filter); } /** * @template T of Node * @param array> $types * @return T|null */ public function findFirstPreviousOfTypes(Node $mainNode, array $types): ?Node { return $this->findFirstPrevious($mainNode, function (Node $node) use ($types): bool { return $this->typeChecker->isInstanceOf($node, $types); }); } public function findFirstNext(Node $node, callable $filter): ?Node { $next = $node->getAttribute(AttributeKey::NEXT_NODE); if ($next instanceof Node) { if ($next instanceof Return_ && $next->expr === null) { return null; } $found = $this->findFirst($next, $filter); if ($found instanceof Node) { return $found; } return $this->findFirstNext($next, $filter); } $parent = $node->getAttribute(AttributeKey::PARENT_NODE); if ($parent instanceof Return_ || $parent instanceof FunctionLike) { return null; } if ($parent instanceof Node) { return $this->findFirstNext($parent, $filter); } return null; } /** * @template T of Node * @param Node|Node[] $nodes * @param class-string $type * @return T|null */ private function findInstanceOfName($nodes, string $type, string $name): ?Node { Assert::isAOf($type, Node::class); $foundInstances = $this->nodeFinder->findInstanceOf($nodes, $type); foreach ($foundInstances as $foundInstance) { if (! $this->nodeNameResolver->isName($foundInstance, $name)) { continue; } return $foundInstance; } return null; } }