diff --git a/README.md b/README.md index 97a3592..280d772 100644 --- a/README.md +++ b/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) { // ... } } diff --git a/lib/PHPParser/NodeTraverser.php b/lib/PHPParser/NodeTraverser.php index a0e1d46..e61a065 100644 --- a/lib/PHPParser/NodeTraverser.php +++ b/lib/PHPParser/NodeTraverser.php @@ -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; } } \ No newline at end of file diff --git a/lib/PHPParser/NodeVisitor.php b/lib/PHPParser/NodeVisitor.php index b3f09e7..339dbb3 100644 --- a/lib/PHPParser/NodeVisitor.php +++ b/lib/PHPParser/NodeVisitor.php @@ -1,32 +1,58 @@ 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;