1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2024-11-30 04:19:30 +01:00

Throw if NodeVisitor returns invalid value

This commit is contained in:
Nikita Popov 2017-02-03 22:30:26 +01:00
parent 2b6804aa50
commit 2b72bae3d9
2 changed files with 89 additions and 40 deletions

View File

@ -104,7 +104,7 @@ class NodeTraverser implements NodeTraverserInterface
foreach ($node->getSubNodeNames() as $name) { foreach ($node->getSubNodeNames() as $name) {
$subNode =& $node->$name; $subNode =& $node->$name;
if (is_array($subNode)) { if (\is_array($subNode)) {
$subNode = $this->traverseArray($subNode); $subNode = $this->traverseArray($subNode);
if ($this->stopTraversal) { if ($this->stopTraversal) {
break; break;
@ -113,13 +113,19 @@ class NodeTraverser implements NodeTraverserInterface
$traverseChildren = true; $traverseChildren = true;
foreach ($this->visitors as $visitor) { foreach ($this->visitors as $visitor) {
$return = $visitor->enterNode($subNode); $return = $visitor->enterNode($subNode);
if (self::DONT_TRAVERSE_CHILDREN === $return) { if (null !== $return) {
if ($return instanceof Node) {
$subNode = $return;
} elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false; $traverseChildren = false;
} else if (self::STOP_TRAVERSAL === $return) { } elseif (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true; $this->stopTraversal = true;
break 2; break 2;
} else if (null !== $return) { } else {
$subNode = $return; throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
} }
} }
@ -132,17 +138,22 @@ class NodeTraverser implements NodeTraverserInterface
foreach ($this->visitors as $visitor) { foreach ($this->visitors as $visitor) {
$return = $visitor->leaveNode($subNode); $return = $visitor->leaveNode($subNode);
if (self::STOP_TRAVERSAL === $return) { if (null !== $return) {
if ($return instanceof Node) {
$subNode = $return;
} elseif (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true; $this->stopTraversal = true;
break 2; break 2;
} else if (null !== $return) { } elseif (\is_array($return)) {
if (is_array($return)) {
throw new \LogicException( throw new \LogicException(
'leaveNode() may only return an array ' . 'leaveNode() may only return an array ' .
'if the parent structure is an array' 'if the parent structure is an array'
); );
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
} }
$subNode = $return;
} }
} }
} }
@ -166,13 +177,19 @@ class NodeTraverser implements NodeTraverserInterface
$traverseChildren = true; $traverseChildren = true;
foreach ($this->visitors as $visitor) { foreach ($this->visitors as $visitor) {
$return = $visitor->enterNode($node); $return = $visitor->enterNode($node);
if (self::DONT_TRAVERSE_CHILDREN === $return) { if (null !== $return) {
if ($return instanceof Node) {
$node = $return;
} elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false; $traverseChildren = false;
} else if (self::STOP_TRAVERSAL === $return) { } elseif (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true; $this->stopTraversal = true;
break 2; break 2;
} else if (null !== $return) { } else {
$node = $return; throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
} }
} }
@ -185,21 +202,26 @@ class NodeTraverser implements NodeTraverserInterface
foreach ($this->visitors as $visitor) { foreach ($this->visitors as $visitor) {
$return = $visitor->leaveNode($node); $return = $visitor->leaveNode($node);
if (null !== $return) {
if (self::REMOVE_NODE === $return) { if ($return instanceof Node) {
$node = $return;
} elseif (\is_array($return)) {
$doNodes[] = array($i, $return);
break;
} elseif (self::REMOVE_NODE === $return) {
$doNodes[] = array($i, array()); $doNodes[] = array($i, array());
break; break;
} else if (self::STOP_TRAVERSAL === $return) { } else if (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true; $this->stopTraversal = true;
break 2; break 2;
} elseif (is_array($return)) { } else {
$doNodes[] = array($i, $return); throw new \LogicException(
break; 'leaveNode() returned invalid value of type ' . gettype($return)
} elseif (null !== $return) { );
$node = $return;
} }
} }
} else if (is_array($node)) { }
} else if (\is_array($node)) {
throw new \LogicException('Invalid node structure: Contains nested arrays'); throw new \LogicException('Invalid node structure: Contains nested arrays');
} }
} }

View File

@ -250,17 +250,44 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
} }
/** /**
* @expectedException \LogicException * @dataProvider provideTestInvalidReturn
* @expectedExceptionMessage leaveNode() may only return an array if the parent structure is an array
*/ */
public function testReplaceByArrayOnlyAllowedIfParentIsArray() { public function testInvalidReturn($visitor, $message) {
$stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42))); $this->setExpectedException(\LogicException::class, $message);
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); $stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42)));
$visitor->method('leaveNode')->willReturn(array(new Node\Scalar\DNumber(42.0)));
$traverser = new NodeTraverser(); $traverser = new NodeTraverser();
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$traverser->traverse($stmts); $traverser->traverse($stmts);
} }
public function provideTestInvalidReturn() {
$visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor1->expects($this->at(1))->method('enterNode')
->will($this->returnValue('foobar'));
$visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor2->expects($this->at(2))->method('enterNode')
->will($this->returnValue('foobar'));
$visitor3 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor3->expects($this->at(3))->method('leaveNode')
->will($this->returnValue('foobar'));
$visitor4 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor4->expects($this->at(4))->method('leaveNode')
->will($this->returnValue('foobar'));
$visitor5 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor5->method('leaveNode')->willReturn(array(new Node\Scalar\DNumber(42.0)));
return [
[$visitor1, 'enterNode() returned invalid value of type string'],
[$visitor2, 'enterNode() returned invalid value of type string'],
[$visitor3, 'leaveNode() returned invalid value of type string'],
[$visitor4, 'leaveNode() returned invalid value of type string'],
[$visitor5, 'leaveNode() may only return an array if the parent structure is an array'],
];
}
} }