mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-11-30 04:19:30 +01:00
Merge branch '3.x'
Conflicts: lib/PhpParser/NodeVisitor.php
This commit is contained in:
commit
5cc2750ebc
@ -13,6 +13,14 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
*/
|
||||
const DONT_TRAVERSE_CHILDREN = 1;
|
||||
|
||||
/**
|
||||
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
|
||||
* STOP_TRAVERSAL, traversal is aborted.
|
||||
*
|
||||
* The afterTraverse() method will still be invoked.
|
||||
*/
|
||||
const STOP_TRAVERSAL = 2;
|
||||
|
||||
/**
|
||||
* If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
|
||||
* in an array, it will be removed from the array.
|
||||
@ -25,6 +33,9 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
/** @var NodeVisitor[] Visitors */
|
||||
protected $visitors;
|
||||
|
||||
/** @var bool Whether traversal should be stopped */
|
||||
protected $stopTraversal;
|
||||
|
||||
/**
|
||||
* Constructs a node traverser.
|
||||
*/
|
||||
@ -63,6 +74,8 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
* @return Node[] Traversed array of nodes
|
||||
*/
|
||||
public function traverse(array $nodes) {
|
||||
$this->stopTraversal = false;
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (null !== $return = $visitor->beforeTraverse($nodes)) {
|
||||
$nodes = $return;
|
||||
@ -93,12 +106,18 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
|
||||
if (is_array($subNode)) {
|
||||
$subNode = $this->traverseArray($subNode);
|
||||
if ($this->stopTraversal) {
|
||||
break;
|
||||
}
|
||||
} elseif ($subNode instanceof Node) {
|
||||
$traverseChildren = true;
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$return = $visitor->enterNode($subNode);
|
||||
if (self::DONT_TRAVERSE_CHILDREN === $return) {
|
||||
$traverseChildren = false;
|
||||
} else if (self::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} else if (null !== $return) {
|
||||
$subNode = $return;
|
||||
}
|
||||
@ -106,10 +125,17 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
|
||||
if ($traverseChildren) {
|
||||
$subNode = $this->traverseNode($subNode);
|
||||
if ($this->stopTraversal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (null !== $return = $visitor->leaveNode($subNode)) {
|
||||
$return = $visitor->leaveNode($subNode);
|
||||
if (self::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} else if (null !== $return) {
|
||||
if (is_array($return)) {
|
||||
throw new \LogicException(
|
||||
'leaveNode() may only return an array ' .
|
||||
@ -138,12 +164,18 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
foreach ($nodes as $i => &$node) {
|
||||
if (is_array($node)) {
|
||||
$node = $this->traverseArray($node);
|
||||
if ($this->stopTraversal) {
|
||||
break;
|
||||
}
|
||||
} elseif ($node instanceof Node) {
|
||||
$traverseChildren = true;
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$return = $visitor->enterNode($node);
|
||||
if (self::DONT_TRAVERSE_CHILDREN === $return) {
|
||||
$traverseChildren = false;
|
||||
} else if (self::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} else if (null !== $return) {
|
||||
$node = $return;
|
||||
}
|
||||
@ -151,6 +183,9 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
|
||||
if ($traverseChildren) {
|
||||
$node = $this->traverseNode($node);
|
||||
if ($this->stopTraversal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
@ -159,6 +194,9 @@ class NodeTraverser implements NodeTraverserInterface
|
||||
if (self::REMOVE_NODE === $return) {
|
||||
$doNodes[] = array($i, array());
|
||||
break;
|
||||
} else if (self::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} elseif (is_array($return)) {
|
||||
$doNodes[] = array($i, $return);
|
||||
break;
|
||||
|
@ -25,12 +25,14 @@ interface NodeVisitor
|
||||
* => $node stays as-is
|
||||
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
|
||||
* => Children of $node are not traversed. $node stays as-is
|
||||
* * NodeTraverser::STOP_TRAVERSAL
|
||||
* => Traversal is aborted. $node stays as-is
|
||||
* * otherwise
|
||||
* => $node is set to the return value
|
||||
*
|
||||
* @param Node $node Node
|
||||
*
|
||||
* @return null|Node|int Replacement node (or special return value)
|
||||
* @return null|int|Node Replacement node (or special return value)
|
||||
*/
|
||||
public function enterNode(Node $node);
|
||||
|
||||
@ -42,6 +44,8 @@ interface NodeVisitor
|
||||
* => $node stays as-is
|
||||
* * NodeTraverser::REMOVE_NODE
|
||||
* => $node is removed from the parent array
|
||||
* * NodeTraverser::STOP_TRAVERSAL
|
||||
* => Traversal is aborted. $node stays as-is
|
||||
* * array (of Nodes)
|
||||
* => The return value is merged into the parent array (at the position of the $node)
|
||||
* * otherwise
|
||||
@ -49,7 +53,7 @@ interface NodeVisitor
|
||||
*
|
||||
* @param Node $node Node
|
||||
*
|
||||
* @return null|Node|false|Node[] Replacement node (or special return value)
|
||||
* @return null|false|int|Node|Node[] Replacement node (or special return value)
|
||||
*/
|
||||
public function leaveNode(Node $node);
|
||||
|
||||
|
@ -166,6 +166,63 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($stmts, $traverser->traverse($stmts));
|
||||
}
|
||||
|
||||
public function testStopTraversal() {
|
||||
$varNode1 = new Expr\Variable('a');
|
||||
$varNode2 = new Expr\Variable('b');
|
||||
$varNode3 = new Expr\Variable('c');
|
||||
$mulNode = new Expr\BinaryOp\Mul($varNode1, $varNode2);
|
||||
$printNode = new Expr\Print_($varNode3);
|
||||
$stmts = [$mulNode, $printNode];
|
||||
|
||||
// From enterNode() with array parent
|
||||
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor->expects($this->at(1))->method('enterNode')->with($mulNode)
|
||||
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
|
||||
$visitor->expects($this->at(2))->method('afterTraversal');
|
||||
$traverser = new NodeTraverser;
|
||||
$traverser->addVisitor($visitor);
|
||||
$this->assertEquals($stmts, $traverser->traverse($stmts));
|
||||
|
||||
// From enterNode with Node parent
|
||||
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor->expects($this->at(2))->method('enterNode')->with($varNode1)
|
||||
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
|
||||
$visitor->expects($this->at(3))->method('afterTraversal');
|
||||
$traverser = new NodeTraverser;
|
||||
$traverser->addVisitor($visitor);
|
||||
$this->assertEquals($stmts, $traverser->traverse($stmts));
|
||||
|
||||
// From leaveNode with Node parent
|
||||
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor->expects($this->at(3))->method('leaveNode')->with($varNode1)
|
||||
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
|
||||
$visitor->expects($this->at(4))->method('afterTraversal');
|
||||
$traverser = new NodeTraverser;
|
||||
$traverser->addVisitor($visitor);
|
||||
$this->assertEquals($stmts, $traverser->traverse($stmts));
|
||||
|
||||
// From leaveNode with array parent
|
||||
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor->expects($this->at(6))->method('leaveNode')->with($mulNode)
|
||||
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
|
||||
$visitor->expects($this->at(7))->method('afterTraversal');
|
||||
$traverser = new NodeTraverser;
|
||||
$traverser->addVisitor($visitor);
|
||||
$this->assertEquals($stmts, $traverser->traverse($stmts));
|
||||
|
||||
// Check that pending array modifications are still carried out
|
||||
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor->expects($this->at(6))->method('leaveNode')->with($mulNode)
|
||||
->will($this->returnValue(NodeTraverser::REMOVE_NODE));
|
||||
$visitor->expects($this->at(7))->method('enterNode')->with($printNode)
|
||||
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
|
||||
$visitor->expects($this->at(8))->method('afterTraversal');
|
||||
$traverser = new NodeTraverser;
|
||||
$traverser->addVisitor($visitor);
|
||||
$this->assertEquals([$printNode], $traverser->traverse($stmts));
|
||||
|
||||
}
|
||||
|
||||
public function testRemovingVisitor() {
|
||||
$visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
$visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
|
||||
|
Loading…
Reference in New Issue
Block a user