mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2025-01-21 21:01:15 +01:00
Don't use references in NodeTraverser
Stop using reference magic in the NodeTraverser. Instead use normal return values. Additionally enfore that an array is passed to ->traverse().
This commit is contained in:
parent
d5f148b43f
commit
d53173c2f3
34
README.md
34
README.md
@ -139,29 +139,29 @@ NodeTraverser
|
||||
-------------
|
||||
|
||||
The node traverser allows traversing the node tree using a visitor class. A visitor class must
|
||||
implement the `NodeVisitorInterface`, which defines the following four methods:
|
||||
implement the `NodeVisitor` interface, which defines the following four methods:
|
||||
|
||||
public function beforeTraverse(&$node);
|
||||
public function enterNode(PHPParser_Node &$node);
|
||||
public function leaveNode(PHPParser_Node &$node);
|
||||
public function afterTraverse(&$node);
|
||||
public function beforeTraverse(array $nodes);
|
||||
public function enterNode(PHPParser_Node $node);
|
||||
public function leaveNode(PHPParser_Node $node);
|
||||
public function afterTraverse(array $nodes);
|
||||
|
||||
The `beforeTraverse` method is called once before the traversal begins and is passed the node the
|
||||
The `beforeTraverse` method is called once before the traversal begins and is passed the nodes the
|
||||
traverser was called with. This method can be used for resetting values before traversation or
|
||||
preparing the tree for traversal.
|
||||
|
||||
The `afterTraverse` method is similar to the `beforeTraverse` method, with the only difference that
|
||||
it is called once after the traversal.
|
||||
|
||||
The `enterNode` and `leaveNode` methods are called on every node, the former when it is entered, i.e.
|
||||
before its subnodes are traversed, the latter when it is left.
|
||||
The `enterNode` and `leaveNode` methods are called on every node, the former when it is entered,
|
||||
i.e. before its subnodes are traversed, the latter when it is left.
|
||||
|
||||
The node is passed into all four functions by reference, i.e. the node may be transformed or even
|
||||
replaced in any way. (As the node is passed by reference it obviously shouldn't be returned after
|
||||
modifiation.) Additionally `leaveNode` can return two special values: If `false` is returned the
|
||||
current node will be completely deleted. If an `array` is returned the current node will be replaced
|
||||
with with an array of other nodes. I.e. if in `array(A, B, C)` the node `B` should be replaced with
|
||||
`array(X, Y, Z)` the result will be `array(A, X, Y, Z, C)`.
|
||||
All four methods can either return the changed node or not return at all (or return `null`) in which
|
||||
case the current node is not changed. The `leaveNode` method can furthermore return two special
|
||||
values: If `false` is returned the current node will be removed from the parent array. If an `array`
|
||||
is returned the current node will be merged into the parent array at the offset of the current node.
|
||||
I.e. if in `array(A, B, C)` the node `B` should be replaced with `array(X, Y, Z)` the result will be
|
||||
`array(A, X, Y, Z, C)`.
|
||||
|
||||
The above described visitors are registered in the `NodeTraverser` class:
|
||||
|
||||
@ -171,15 +171,13 @@ The above described visitors are registered in the `NodeTraverser` class:
|
||||
$traverser->addVisitor($visitor);
|
||||
|
||||
$stmts = $parser->parse($lexer);
|
||||
|
||||
// ->traverse() directly modifies $stmts. Do *not* write $stmts = $traverser->traverse($stmts);
|
||||
$traverser->traverse($stmts);
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
|
||||
With `MyVisitor` being something like that:
|
||||
|
||||
class MyVisitor extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public function enterNode(PHPParser_Node &$node) {
|
||||
public function enterNode(PHPParser_Node $node) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
class PHPParser_NodeTraverser
|
||||
{
|
||||
/**
|
||||
* @var PHPParser_NodeVisitorInterface[] Visitors
|
||||
* @var PHPParser_NodeVisitor[] Visitors
|
||||
*/
|
||||
protected $visitors;
|
||||
|
||||
@ -17,61 +17,69 @@ class PHPParser_NodeTraverser
|
||||
/**
|
||||
* Adds a visitor.
|
||||
*
|
||||
* @param PHPParser_NodeVisitorInterface $visitor Visitor to add
|
||||
* @param PHPParser_NodeVisitor $visitor Visitor to add
|
||||
*/
|
||||
public function addVisitor(PHPParser_NodeVisitorInterface $visitor) {
|
||||
public function addVisitor(PHPParser_NodeVisitor $visitor) {
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses a node or an array using the registered visitors.
|
||||
* Traverses an array of nodes using the registered visitors.
|
||||
*
|
||||
* @param array|PHPParser_Node $node Node or array
|
||||
* @param PHPParser_Node[] $nodes Array of nodes
|
||||
*
|
||||
* @return PHPParser_Node[] Traversed array of nodes
|
||||
*/
|
||||
public function traverse(&$node) {
|
||||
if (!is_array($node) && !$node instanceof PHPParser_Node) {
|
||||
throw new InvalidArgumentException('Can only traverse nodes and arrays');
|
||||
public function traverse(array $nodes) {
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (null !== $return = $visitor->beforeTraverse($nodes)) {
|
||||
$nodes = $return;
|
||||
}
|
||||
}
|
||||
|
||||
$nodes = $this->_traverse($nodes);
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$visitor->beforeTraverse($node);
|
||||
if (null !== $return = $visitor->afterTraverse($nodes)) {
|
||||
$nodes = $return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_traverse($node, $this->visitors);
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$visitor->afterTraverse($node);
|
||||
}
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
protected function _traverse(&$node, array $visitors) {
|
||||
protected function _traverse($node) {
|
||||
$doNodes = array();
|
||||
|
||||
foreach ($node as $subNodeKey => &$subNode) {
|
||||
foreach ($node as $name => $subNode) {
|
||||
if (is_array($subNode)) {
|
||||
$this->_traverse($subNode, $visitors);
|
||||
$node[$name] = $this->_traverse($subNode, $this->visitors);
|
||||
} elseif ($subNode instanceof PHPParser_Node) {
|
||||
foreach ($visitors as $visitor) {
|
||||
$visitor->enterNode($subNode);
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (null !== $return = $visitor->enterNode($subNode)) {
|
||||
$node[$name] = $return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_traverse($subNode, $visitors);
|
||||
$node[$name] = $this->_traverse($subNode, $this->visitors);
|
||||
|
||||
foreach ($visitors as $i => $visitor) {
|
||||
foreach ($this->visitors as $i => $visitor) {
|
||||
$return = $visitor->leaveNode($subNode);
|
||||
|
||||
if (false === $return) {
|
||||
$doNodes[] = array($subNodeKey, array());
|
||||
$doNodes[] = array($name, array());
|
||||
break;
|
||||
} elseif (is_array($return)) {
|
||||
// traverse replacement nodes using all visitors apart from the one that
|
||||
// did the change
|
||||
$subNodeVisitors = $visitors;
|
||||
unset($subNodeVisitors[$i]);
|
||||
$this->_traverse($return, $subNodeVisitors);
|
||||
unset($this->visitors[$i]);
|
||||
$return = $this->_traverse($return);
|
||||
$this->visitors[$i] = $visitor;
|
||||
|
||||
$doNodes[] = array($subNodeKey, $return);
|
||||
$doNodes[] = array($name, $return);
|
||||
break;
|
||||
} elseif (null !== $return) {
|
||||
$node[$name] = $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,5 +94,7 @@ class PHPParser_NodeTraverser
|
||||
array_splice($node, $key, 1, $replace);
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
@ -1,32 +1,58 @@
|
||||
<?php
|
||||
|
||||
interface PHPParser_NodeVisitorInterface
|
||||
interface PHPParser_NodeVisitor
|
||||
{
|
||||
/**
|
||||
* Called once before traversal.
|
||||
*
|
||||
* @param $node
|
||||
* Return value semantics:
|
||||
* * null: $nodes stays as-is
|
||||
* * otherwise: $nodes is set to the return value
|
||||
*
|
||||
* @param PHPParser_Node[] $nodes Array of nodes
|
||||
*
|
||||
* @return null|PHPParser_Node[] Array of nodes
|
||||
*/
|
||||
public function beforeTraverse($node);
|
||||
public function beforeTraverse(array $nodes);
|
||||
|
||||
/**
|
||||
* Called when entering a node.
|
||||
*
|
||||
* @param PHPParser_Node $node
|
||||
* Return value semantics:
|
||||
* * null: $node stays as-is
|
||||
* * otherwise: $node is set to the return value
|
||||
*
|
||||
* @param PHPParser_Node $node Node
|
||||
*
|
||||
* @return null|PHPParser_Node Node
|
||||
*/
|
||||
public function enterNode(PHPParser_Node $node);
|
||||
|
||||
/**
|
||||
* Called when leaving a node.
|
||||
*
|
||||
* @param PHPParser_Node $node
|
||||
* Return value semantics:
|
||||
* * null: $node stays as-is
|
||||
* * false: $node is removed from the parent array
|
||||
* * array: The return value is merged into the parent array (at the position of the $node)
|
||||
* * otherwise: $node is set to the return value
|
||||
*
|
||||
* @param PHPParser_Node $node Node
|
||||
*
|
||||
* @return null|PHPParser_Node|false|PHPParser_Node[] Node
|
||||
*/
|
||||
public function leaveNode(PHPParser_Node $node);
|
||||
|
||||
/**
|
||||
* Called once after traversal.
|
||||
*
|
||||
* @param $node
|
||||
* Return value semantics:
|
||||
* * null: $nodes stays as-is
|
||||
* * otherwise: $nodes is set to the return value
|
||||
*
|
||||
* @param PHPParser_Node[] $nodes Array of nodes
|
||||
*
|
||||
* @return null|PHPParser_Node[] Array of nodes
|
||||
*/
|
||||
public function afterTraverse($node);
|
||||
public function afterTraverse(array $nodes);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
class PHPParser_NodeVisitorAbstract implements PHPParser_NodeVisitorInterface
|
||||
class PHPParser_NodeVisitorAbstract implements PHPParser_NodeVisitor
|
||||
{
|
||||
public function beforeTraverse(&$node) { }
|
||||
public function enterNode(PHPParser_Node &$node) { }
|
||||
public function leaveNode(PHPParser_Node &$node) { }
|
||||
public function afterTraverse(&$node) { }
|
||||
public function beforeTraverse(array $nodes) { }
|
||||
public function enterNode(PHPParser_Node $node) { }
|
||||
public function leaveNode(PHPParser_Node $node) { }
|
||||
public function afterTraverse(array $nodes) { }
|
||||
}
|
@ -21,13 +21,14 @@ class PHPParser_Tests_NodeTraverserTest extends PHPUnit_Framework_TestCase
|
||||
public function testTraverse() {
|
||||
$node = $this->getTestNode();
|
||||
|
||||
$visitor = new PHPParser_Tests_NodeVisitor;
|
||||
$visitor = new PHPParser_Tests_NodeVisitor;
|
||||
|
||||
$traverser = new PHPParser_NodeTraverser;
|
||||
|
||||
$traverser->addVisitor($visitor);
|
||||
$traverser->traverse($node);
|
||||
|
||||
$this->assertEquals($node, $visitor->beforeTraverseNode);
|
||||
$node = $traverser->traverse($node);
|
||||
|
||||
$this->assertEquals($node, $visitor->beforeTraverseNodes);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
@ -53,17 +54,14 @@ class PHPParser_Tests_NodeTraverserTest extends PHPUnit_Framework_TestCase
|
||||
$visitor->leftNodes
|
||||
);
|
||||
|
||||
$this->assertEquals($node, $visitor->afterTraverseNode);
|
||||
$this->assertEquals($node, $visitor->afterTraverseNodes);
|
||||
}
|
||||
|
||||
public function testModifyingTraverse() {
|
||||
$node = $this->getTestNode();
|
||||
|
||||
$visitor = new PHPParser_Tests_ModifyingNodeVisitor;
|
||||
$traverser = new PHPParser_NodeTraverser;
|
||||
|
||||
$traverser->addVisitor($visitor);
|
||||
$traverser->traverse($node);
|
||||
$traverser->addVisitor(new PHPParser_Tests_ModifyingNodeVisitor);
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
@ -71,42 +69,42 @@ class PHPParser_Tests_NodeTraverserTest extends PHPUnit_Framework_TestCase
|
||||
new PHPParser_Node_Scalar_String('Foo Bar')
|
||||
)),
|
||||
),
|
||||
$node
|
||||
$traverser->traverse($node)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PHPParser_Tests_NodeVisitor extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public $beforeTraverseNode;
|
||||
public $beforeTraverseNodes;
|
||||
public $enteredNodes;
|
||||
public $leftNodes;
|
||||
public $afterTraverseNode;
|
||||
public $afterTraverseNodes;
|
||||
|
||||
public function __construct() {
|
||||
$this->enteredNodes = $this->leftNodes = array();
|
||||
}
|
||||
|
||||
public function beforeTraverse(&$node) {
|
||||
$this->beforeTraverseNode = $node;
|
||||
public function beforeTraverse(array $nodes) {
|
||||
$this->beforeTraverseNodes = $nodes;
|
||||
}
|
||||
|
||||
public function enterNode(PHPParser_Node &$node) {
|
||||
public function enterNode(PHPParser_Node $node) {
|
||||
$this->enteredNodes[] = $node->getType();
|
||||
}
|
||||
|
||||
public function leaveNode(PHPParser_Node &$node) {
|
||||
public function leaveNode(PHPParser_Node $node) {
|
||||
$this->leftNodes[] = $node->getType();
|
||||
}
|
||||
|
||||
public function afterTraverse(&$node) {
|
||||
$this->afterTraverseNode = $node;
|
||||
public function afterTraverse(array $nodes) {
|
||||
$this->afterTraverseNodes = $nodes;
|
||||
}
|
||||
}
|
||||
|
||||
class PHPParser_Tests_ModifyingNodeVisitor extends PHPParser_NodeVisitorAbstract
|
||||
{
|
||||
public function leaveNode(PHPParser_Node &$node) {
|
||||
public function leaveNode(PHPParser_Node $node) {
|
||||
// delete namespace nodes by merging them
|
||||
if ($node instanceof PHPParser_Node_Stmt_Namespace) {
|
||||
return $node->stmts;
|
||||
|
Loading…
x
Reference in New Issue
Block a user