mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-01-16 14:48:32 +01:00
parent
289756d056
commit
afe1628a72
@ -116,6 +116,9 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
|
||||
$subNode = null;
|
||||
continue 2;
|
||||
} else {
|
||||
throw new \LogicException(
|
||||
'enterNode() returned invalid value of type ' . gettype($return)
|
||||
@ -142,6 +145,9 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
|
||||
$subNode = null;
|
||||
break;
|
||||
} elseif (\is_array($return)) {
|
||||
throw new \LogicException(
|
||||
'leaveNode() may only return an array ' .
|
||||
@ -195,6 +201,9 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
|
||||
throw new \LogicException(
|
||||
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
|
||||
} else {
|
||||
throw new \LogicException(
|
||||
'enterNode() returned invalid value of type ' . gettype($return)
|
||||
@ -227,6 +236,9 @@ class NodeTraverser implements NodeTraverserInterface {
|
||||
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
|
||||
$this->stopTraversal = true;
|
||||
break 2;
|
||||
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
|
||||
throw new \LogicException(
|
||||
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
|
||||
} else {
|
||||
throw new \LogicException(
|
||||
'leaveNode() returned invalid value of type ' . gettype($return)
|
||||
|
@ -38,6 +38,13 @@ interface NodeVisitor {
|
||||
*/
|
||||
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
|
||||
|
||||
/**
|
||||
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns REPLACE_WITH_NULL,
|
||||
* the node will be replaced with null. This is not a legal return value if the node is part
|
||||
* of an array, rather than another node.
|
||||
*/
|
||||
public const REPLACE_WITH_NULL = 5;
|
||||
|
||||
/**
|
||||
* Called once before traversal.
|
||||
*
|
||||
@ -59,14 +66,16 @@ interface NodeVisitor {
|
||||
* => $node stays as-is
|
||||
* * array (of Nodes)
|
||||
* => The return value is merged into the parent array (at the position of the $node)
|
||||
* * NodeTraverser::REMOVE_NODE
|
||||
* * NodeVisitor::REMOVE_NODE
|
||||
* => $node is removed from the parent array
|
||||
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
|
||||
* * NodeVisitor::REPLACE_WITH_NULL
|
||||
* => $node is replaced with null
|
||||
* * NodeVisitor::DONT_TRAVERSE_CHILDREN
|
||||
* => Children of $node are not traversed. $node stays as-is
|
||||
* * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN
|
||||
* * NodeVisitor::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
|
||||
* * NodeVisitor::STOP_TRAVERSAL
|
||||
* => Traversal is aborted. $node stays as-is
|
||||
* * otherwise
|
||||
* => $node is set to the return value
|
||||
@ -83,9 +92,11 @@ interface NodeVisitor {
|
||||
* Return value semantics:
|
||||
* * null
|
||||
* => $node stays as-is
|
||||
* * NodeTraverser::REMOVE_NODE
|
||||
* * NodeVisitor::REMOVE_NODE
|
||||
* => $node is removed from the parent array
|
||||
* * NodeTraverser::STOP_TRAVERSAL
|
||||
* * NodeVisitor::REPLACE_WITH_NULL
|
||||
* => $node is replaced with null
|
||||
* * NodeVisitor::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)
|
||||
|
@ -3,7 +3,10 @@
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Scalar\Int_;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Else_;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
|
||||
class NodeTraverserTest extends \PHPUnit\Framework\TestCase {
|
||||
public function testNonModifying() {
|
||||
@ -343,6 +346,44 @@ class NodeTraverserTest extends \PHPUnit\Framework\TestCase {
|
||||
], $visitor->trace);
|
||||
}
|
||||
|
||||
public function testReplaceWithNull() {
|
||||
$one = new Int_(1);
|
||||
$else1 = new Else_();
|
||||
$else2 = new Else_();
|
||||
$if1 = new If_($one, ['else' => $else1]);
|
||||
$if2 = new If_($one, ['else' => $else2]);
|
||||
$stmts = [$if1, $if2];
|
||||
$visitor1 = new NodeVisitorForTesting([
|
||||
['enterNode', $else1, NodeVisitor::REPLACE_WITH_NULL],
|
||||
['leaveNode', $else2, NodeVisitor::REPLACE_WITH_NULL],
|
||||
]);
|
||||
$visitor2 = new NodeVisitorForTesting();
|
||||
$traverser = new NodeTraverser();
|
||||
$traverser->addVisitor($visitor1);
|
||||
$traverser->addVisitor($visitor2);
|
||||
$newStmts = $traverser->traverse($stmts);
|
||||
$this->assertEquals([
|
||||
new If_($one),
|
||||
new If_($one),
|
||||
], $newStmts);
|
||||
$this->assertEquals([
|
||||
['beforeTraverse', $stmts],
|
||||
['enterNode', $if1],
|
||||
['enterNode', $one],
|
||||
// We never see the if1 Else node.
|
||||
['leaveNode', $one],
|
||||
['leaveNode', $if1],
|
||||
['enterNode', $if2],
|
||||
['enterNode', $one],
|
||||
['leaveNode', $one],
|
||||
// We do see the if2 Else node, as it will only be replaced afterwards.
|
||||
['enterNode', $else2],
|
||||
['leaveNode', $else2],
|
||||
['leaveNode', $if2],
|
||||
['afterTraverse', $stmts],
|
||||
], $visitor2->trace);
|
||||
}
|
||||
|
||||
public function testRemovingVisitor() {
|
||||
$visitor1 = new class () extends NodeVisitorAbstract {};
|
||||
$visitor2 = new class () extends NodeVisitorAbstract {};
|
||||
@ -415,6 +456,12 @@ class NodeTraverserTest extends \PHPUnit\Framework\TestCase {
|
||||
$visitor8 = new NodeVisitorForTesting([
|
||||
['enterNode', $num, new Node\Stmt\Return_()],
|
||||
]);
|
||||
$visitor9 = new NodeVisitorForTesting([
|
||||
['enterNode', $expr, NodeVisitor::REPLACE_WITH_NULL],
|
||||
]);
|
||||
$visitor10 = new NodeVisitorForTesting([
|
||||
['leaveNode', $expr, NodeVisitor::REPLACE_WITH_NULL],
|
||||
]);
|
||||
|
||||
return [
|
||||
[$stmts, $visitor1, 'enterNode() returned invalid value of type string'],
|
||||
@ -425,6 +472,8 @@ class NodeTraverserTest extends \PHPUnit\Framework\TestCase {
|
||||
[$stmts, $visitor6, 'leaveNode() returned invalid value of type bool'],
|
||||
[$stmts, $visitor7, 'Trying to replace statement (Stmt_Expression) with expression (Scalar_Int). Are you missing a Stmt_Expression wrapper?'],
|
||||
[$stmts, $visitor8, 'Trying to replace expression (Scalar_Int) with statement (Stmt_Return)'],
|
||||
[$stmts, $visitor9, 'REPLACE_WITH_NULL can not be used if the parent structure is an array'],
|
||||
[$stmts, $visitor10, 'REPLACE_WITH_NULL can not be used if the parent structure is an array'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user