Stop using ArrayObject for nodes

Instead manually implement IteratorAggregate and define the required magic methods. The reasoning behind this is:
a) Extending ArrayObject is always risky, because a lot of magic which is known to be buggy is involved
b) This allows to lateron change the implementation for the nodes altogether, for example it could be changed to using real public fields instead of a $subNodes array.
This commit is contained in:
nikic 2011-10-28 23:12:32 +02:00
parent 126efbc056
commit c7c94f38f1
2 changed files with 63 additions and 25 deletions

View File

@ -1,7 +1,8 @@
<?php
abstract class PHPParser_NodeAbstract extends ArrayObject implements PHPParser_Node
abstract class PHPParser_NodeAbstract implements PHPParser_Node, IteratorAggregate
{
protected $subNodes;
protected $line;
protected $docComment;
@ -13,8 +14,7 @@ abstract class PHPParser_NodeAbstract extends ArrayObject implements PHPParser_N
* @param null|string $docComment Nearest doc comment
*/
public function __construct(array $subNodes, $line = -1, $docComment = null) {
parent::__construct($subNodes, ArrayObject::ARRAY_AS_PROPS);
$this->subNodes = $subNodes;
$this->line = $line;
$this->docComment = $docComment;
}
@ -45,4 +45,22 @@ abstract class PHPParser_NodeAbstract extends ArrayObject implements PHPParser_N
public function getDocComment() {
return $this->docComment;
}
/* Magic interfaces */
public function &__get($name) {
return $this->subNodes[$name];
}
public function __set($name, $value) {
$this->subNodes[$name] = $value;
}
public function __isset($name) {
return isset($this->subNodes[$name]);
}
public function __unset($name) {
unset($this->subNodes[$name]);
}
public function getIterator() {
return new ArrayIterator($this->subNodes);
}
}

View File

@ -37,7 +37,7 @@ class PHPParser_NodeTraverser
}
}
$nodes = $this->_traverse($nodes);
$nodes = $this->traverseArray($nodes);
foreach ($this->visitors as $visitor) {
if (null !== $return = $visitor->afterTraverse($nodes)) {
@ -48,53 +48,73 @@ class PHPParser_NodeTraverser
return $nodes;
}
protected function _traverse($node) {
$doNodes = array();
protected function traverseNode(PHPParser_Node $node) {
foreach ($node as $name => $subNode) {
if (is_array($subNode)) {
$node[$name] = $this->_traverse($subNode, $this->visitors);
$node->$name = $this->traverseArray($subNode);
} elseif ($subNode instanceof PHPParser_Node) {
foreach ($this->visitors as $visitor) {
if (null !== $return = $visitor->enterNode($subNode)) {
$node[$name] = $return;
$node->$name = $return;
}
}
$node[$name] = $this->_traverse($subNode, $this->visitors);
$node->$name = $this->traverseNode($subNode);
foreach ($this->visitors as $i => $visitor) {
$return = $visitor->leaveNode($subNode);
foreach ($this->visitors as $visitor) {
if (null !== $return = $visitor->leaveNode($subNode)) {
$node->$name = $return;
}
}
}
}
return $node;
}
protected function traverseArray(array $nodes) {
$doNodes = array();
foreach ($nodes as $i => $node) {
if (is_array($node)) {
$nodes[$i] = $this->traverseArray($node);
} elseif ($node instanceof PHPParser_Node) {
foreach ($this->visitors as $visitor) {
if (null !== $return = $visitor->enterNode($node)) {
$nodes[$i] = $return;
}
}
$nodes[$i] = $this->traverseNode($node);
foreach ($this->visitors as $j => $visitor) {
$return = $visitor->leaveNode($node);
if (false === $return) {
$doNodes[] = array($name, array());
$doNodes[] = array($i, array());
break;
} elseif (is_array($return)) {
// traverse replacement nodes using all visitors apart from the one that
// did the change
unset($this->visitors[$i]);
$return = $this->_traverse($return);
$this->visitors[$i] = $visitor;
unset($this->visitors[$j]);
$return = $this->traverseArray($return);
$this->visitors[$j] = $visitor;
$doNodes[] = array($name, $return);
$doNodes[] = array($i, $return);
break;
} elseif (null !== $return) {
$node[$name] = $return;
$nodes[$i] = $return;
}
}
}
}
if (!empty($doNodes)) {
if (!is_array($node)) {
throw new Exception('Nodes can only be merged if the parent is an array');
}
while (list($key, $replace) = array_pop($doNodes)) {
array_splice($node, $key, 1, $replace);
while (list($i, $replace) = array_pop($doNodes)) {
array_splice($nodes, $i, 1, $replace);
}
}
return $node;
return $nodes;
}
}