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:
parent
5cc2750ebc
commit
79afd56565
80
lib/PhpParser/NodeFinder.php
Normal file
80
lib/PhpParser/NodeFinder.php
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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 */
|
47
lib/PhpParser/NodeVisitor/FirstFindingVisitor.php
Normal file
47
lib/PhpParser/NodeVisitor/FirstFindingVisitor.php
Normal 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;
|
||||
}
|
||||
}
|
58
test/PhpParser/NodeFinderTest.php
Normal file
58
test/PhpParser/NodeFinderTest.php
Normal 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));
|
||||
}
|
||||
}
|
@ -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);
|
38
test/PhpParser/NodeVisitor/FirstFindingVisitorTest.php
Normal file
38
test/PhpParser/NodeVisitor/FirstFindingVisitorTest.php
Normal 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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user