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

View File

@ -250,17 +250,44 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage leaveNode() may only return an array if the parent structure is an array
* @dataProvider provideTestInvalidReturn
*/
public function testReplaceByArrayOnlyAllowedIfParentIsArray() {
$stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42)));
public function testInvalidReturn($visitor, $message) {
$this->setExpectedException(\LogicException::class, $message);
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->method('leaveNode')->willReturn(array(new Node\Scalar\DNumber(42.0)));
$stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42)));
$traverser = new NodeTraverser();
$traverser->addVisitor($visitor);
$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'],
];
}
}