diff --git a/lib/PhpParser/NodeTraverser.php b/lib/PhpParser/NodeTraverser.php index 781caa58..8888153a 100644 --- a/lib/PhpParser/NodeTraverser.php +++ b/lib/PhpParser/NodeTraverser.php @@ -201,6 +201,9 @@ class NodeTraverser implements NodeTraverserInterface { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); $node = $return; + } else if (self::REMOVE_NODE === $return) { + $doNodes[] = [$i, []]; + continue 2; } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = false; } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { diff --git a/lib/PhpParser/NodeVisitor.php b/lib/PhpParser/NodeVisitor.php index 5cd92341..39cd0699 100644 --- a/lib/PhpParser/NodeVisitor.php +++ b/lib/PhpParser/NodeVisitor.php @@ -22,8 +22,13 @@ interface NodeVisitor { * Return value semantics: * * null * => $node stays as-is + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array * * NodeTraverser::DONT_TRAVERSE_CHILDREN * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN + * => Further visitors for the current node are skipped, and its children are not + * traversed. $node stays as-is. * * NodeTraverser::STOP_TRAVERSAL * => Traversal is aborted. $node stays as-is * * otherwise diff --git a/test/PhpParser/NodeTraverserTest.php b/test/PhpParser/NodeTraverserTest.php index 27f77c8d..b45ee2c0 100644 --- a/test/PhpParser/NodeTraverserTest.php +++ b/test/PhpParser/NodeTraverserTest.php @@ -61,18 +61,51 @@ class NodeTraverserTest extends \PHPUnit\Framework\TestCase { ], $visitor2->trace); } - public function testRemove() { + public function testRemoveFromLeave() { $str1Node = new String_('Foo'); $str2Node = new String_('Bar'); $visitor = new NodeVisitorForTesting([ ['leaveNode', $str1Node, NodeTraverser::REMOVE_NODE], ]); + $visitor2 = new NodeVisitorForTesting(); $traverser = new NodeTraverser(); $traverser->addVisitor($visitor); + $traverser->addVisitor($visitor2); - $this->assertEquals([$str2Node], $traverser->traverse([$str1Node, $str2Node])); + $stmts = [$str1Node, $str2Node]; + $this->assertEquals([$str2Node], $traverser->traverse($stmts)); + $this->assertEquals([ + ['beforeTraverse', $stmts], + ['enterNode', $str1Node], + ['enterNode', $str2Node], + ['leaveNode', $str2Node], + ['afterTraverse', [$str2Node]], + ], $visitor2->trace); + } + + public function testRemoveFromEnter() { + $str1Node = new String_('Foo'); + $str2Node = new String_('Bar'); + + $visitor = new NodeVisitorForTesting([ + ['enterNode', $str1Node, NodeTraverser::REMOVE_NODE], + ]); + $visitor2 = new NodeVisitorForTesting(); + + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor); + $traverser->addVisitor($visitor2); + + $stmts = [$str1Node, $str2Node]; + $this->assertEquals([$str2Node], $traverser->traverse($stmts)); + $this->assertEquals([ + ['beforeTraverse', $stmts], + ['enterNode', $str2Node], + ['leaveNode', $str2Node], + ['afterTraverse', [$str2Node]], + ], $visitor2->trace); } public function testMerge() {