1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2025-01-22 05:41:23 +01:00

Add NodeFinder class

To simplify basic node finding operations.
This commit is contained in:
Nikita Popov 2017-01-29 23:20:53 +01:00
parent 5cc2750ebc
commit 79afd56565
6 changed files with 227 additions and 4 deletions

View File

@ -0,0 +1,80 @@
<?php
namespace PhpParser;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;
class NodeFinder {
/**
* Find all nodes satisfying a filter callback.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param callable $filter Filter callback: function(Node $node) : bool
*
* @return Node[] Found nodes satisfying the filter callback
*/
public function find($nodes, callable $filter) {
if (!is_array($nodes)) {
$nodes = [$nodes];
}
$visitor = new FindingVisitor($filter);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNodes();
}
/**
* Find all nodes that are instances of a certain class.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param string $class Class name
*
* @return Node[] Found nodes (all instances of $class)
*/
public function findInstanceOf($nodes, $class) {
return $this->find($nodes, function ($node) use ($class) {
return $node instanceof $class;
});
}
/**
* Find first node satisfying a filter callback.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param callable $filter Filter callback: function(Node $node) : bool
*
* @return null|Node Found node (or null if none found)
*/
public function findFirst($nodes, callable $filter) {
if (!is_array($nodes)) {
$nodes = [$nodes];
}
$visitor = new FirstFindingVisitor($filter);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNode();
}
/**
* Find first node that is an instance of a certain class.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param string $class Class name
*
* @return null|Node Found node, which is an instance of $class (or null if none found)
*/
public function findFirstInstanceOf($nodes, $class) {
return $this->findFirst($nodes, function ($node) use ($class) {
return $node instanceof $class;
});
}
}

View File

@ -9,7 +9,7 @@ use PhpParser\NodeVisitorAbstract;
* This visitor can be used to find and collect all nodes satisfying some criterion determined by
* a filter callback.
*/
class FinderVisitor extends NodeVisitorAbstract {
class FindingVisitor extends NodeVisitorAbstract {
/** @var callable Filter callback */
protected $filterCallback;
/** @var Node[] Found nodes */

View File

@ -0,0 +1,47 @@
<?php
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
/**
* This visitor can be used to find the first node satisfying some criterion determined by
* a filter callback.
*/
class FirstFindingVisitor extends NodeVisitorAbstract {
/** @var callable Filter callback */
protected $filterCallback;
/** @var null|Node Found node */
protected $foundNode;
public function __construct(callable $filterCallback) {
$this->filterCallback = $filterCallback;
}
/**
* Get found node satisfying the filter callback.
*
* Returns null if no node satisfies the filter callback.
*
* @return null|Node Found node (or null if not found)
*/
public function getFoundNode() {
return $this->foundNode;
}
public function beforeTraverse(array $nodes) {
$this->foundNode = null;
}
public function enterNode(Node $node) {
$filterCallback = $this->filterCallback;
if ($filterCallback($node)) {
$this->foundNode = $node;
return NodeTraverser::STOP_TRAVERSAL;
}
return null;
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace PhpParser;
use PhpParser\Node\Expr;
class NodeFinderTest extends \PHPUnit_Framework_TestCase {
private function getStmtsAndVars() {
$assign = new Expr\Assign(new Expr\Variable('a'), new Expr\BinaryOp\Concat(
new Expr\Variable('b'), new Expr\Variable('c')
));
$stmts = [new Node\Stmt\Expression($assign)];
$vars = [$assign->var, $assign->expr->left, $assign->expr->right];
return [$stmts, $vars];
}
public function testFind() {
$finder = new NodeFinder;
list($stmts, $vars) = $this->getStmtsAndVars();
$varFilter = function(Node $node) {
return $node instanceof Expr\Variable;
};
$this->assertSame($vars, $finder->find($stmts, $varFilter));
$this->assertSame($vars, $finder->find($stmts[0], $varFilter));
$noneFilter = function () { return false; };
$this->assertSame([], $finder->find($stmts, $noneFilter));
}
public function testFindInstanceOf() {
$finder = new NodeFinder;
list($stmts, $vars) = $this->getStmtsAndVars();
$this->assertSame($vars, $finder->findInstanceOf($stmts, Expr\Variable::class));
$this->assertSame($vars, $finder->findInstanceOf($stmts[0], Expr\Variable::class));
$this->assertSame([], $finder->findInstanceOf($stmts, Expr\BinaryOp\Mul::class));
}
public function testFindFirst() {
$finder = new NodeFinder;
list($stmts, $vars) = $this->getStmtsAndVars();
$varFilter = function(Node $node) {
return $node instanceof Expr\Variable;
};
$this->assertSame($vars[0], $finder->findFirst($stmts, $varFilter));
$this->assertSame($vars[0], $finder->findFirst($stmts[0], $varFilter));
$noneFilter = function () { return false; };
$this->assertSame(null, $finder->findFirst($stmts, $noneFilter));
}
public function testFindFirstInstanceOf() {
$finder = new NodeFinder;
list($stmts, $vars) = $this->getStmtsAndVars();
$this->assertSame($vars[0], $finder->findFirstInstanceOf($stmts, Expr\Variable::class));
$this->assertSame($vars[0], $finder->findFirstInstanceOf($stmts[0], Expr\Variable::class));
$this->assertSame(null, $finder->findFirstInstanceOf($stmts, Expr\BinaryOp\Mul::class));
}
}

View File

@ -6,10 +6,10 @@ use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\NodeTraverser;
class FinderVisitorTest extends \PHPUnit_Framework_TestCase {
class FindingVisitorTest extends \PHPUnit_Framework_TestCase {
public function testFindVariables() {
$traverser = new NodeTraverser();
$visitor = new FinderVisitor(function(Node $node) {
$visitor = new FindingVisitor(function(Node $node) {
return $node instanceof Node\Expr\Variable;
});
$traverser->addVisitor($visitor);
@ -29,7 +29,7 @@ class FinderVisitorTest extends \PHPUnit_Framework_TestCase {
public function testFindAll() {
$traverser = new NodeTraverser();
$visitor = new FinderVisitor(function(Node $node) {
$visitor = new FindingVisitor(function(Node $node) {
return true; // All nodes
});
$traverser->addVisitor($visitor);

View File

@ -0,0 +1,38 @@
<?php
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\NodeTraverser;
class FirstFindingVisitorTest extends \PHPUnit_Framework_TestCase
{
public function testFindFirstVariable() {
$traverser = new NodeTraverser();
$visitor = new FirstFindingVisitor(function(Node $node) {
return $node instanceof Node\Expr\Variable;
});
$traverser->addVisitor($visitor);
$assign = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b'));
$stmts = [new Node\Stmt\Expression($assign)];
$traverser->traverse($stmts);
$this->assertSame($assign->var, $visitor->getFoundNode());
}
public function testFindNone() {
$traverser = new NodeTraverser();
$visitor = new FirstFindingVisitor(function(Node $node) {
return $node instanceof Node\Expr\BinaryOp;
});
$traverser->addVisitor($visitor);
$assign = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b'));
$stmts = [new Node\Stmt\Expression($assign)];
$traverser->traverse($stmts);
$this->assertSame(null, $visitor->getFoundNode());
}
}