Chris@0: visitors = array(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a visitor. Chris@0: * Chris@0: * @param NodeVisitor $visitor Visitor to add Chris@0: */ Chris@0: public function addVisitor(NodeVisitor $visitor) { Chris@0: $this->visitors[] = $visitor; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Removes an added visitor. Chris@0: * Chris@0: * @param NodeVisitor $visitor Chris@0: */ Chris@0: public function removeVisitor(NodeVisitor $visitor) { Chris@0: foreach ($this->visitors as $index => $storedVisitor) { Chris@0: if ($storedVisitor === $visitor) { Chris@0: unset($this->visitors[$index]); Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Traverses an array of nodes using the registered visitors. Chris@0: * Chris@0: * @param Node[] $nodes Array of nodes Chris@0: * Chris@0: * @return Node[] Traversed array of nodes Chris@0: */ Chris@0: public function traverse(array $nodes) { Chris@0: $this->stopTraversal = false; Chris@0: Chris@0: foreach ($this->visitors as $visitor) { Chris@0: if (null !== $return = $visitor->beforeTraverse($nodes)) { Chris@0: $nodes = $return; Chris@0: } Chris@0: } Chris@0: Chris@0: $nodes = $this->traverseArray($nodes); Chris@0: Chris@0: foreach ($this->visitors as $visitor) { Chris@0: if (null !== $return = $visitor->afterTraverse($nodes)) { Chris@0: $nodes = $return; Chris@0: } Chris@0: } Chris@0: Chris@0: return $nodes; Chris@0: } Chris@0: Chris@0: protected function traverseNode(Node $node) { Chris@0: foreach ($node->getSubNodeNames() as $name) { Chris@0: $subNode =& $node->$name; Chris@0: Chris@0: if (is_array($subNode)) { Chris@0: $subNode = $this->traverseArray($subNode); Chris@0: if ($this->stopTraversal) { Chris@0: break; Chris@0: } Chris@0: } elseif ($subNode instanceof Node) { Chris@0: $traverseChildren = true; Chris@0: foreach ($this->visitors as $visitor) { Chris@0: $return = $visitor->enterNode($subNode); Chris@0: if (self::DONT_TRAVERSE_CHILDREN === $return) { Chris@0: $traverseChildren = false; Chris@0: } else if (self::STOP_TRAVERSAL === $return) { Chris@0: $this->stopTraversal = true; Chris@0: break 2; Chris@0: } else if (null !== $return) { Chris@0: $subNode = $return; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($traverseChildren) { Chris@0: $subNode = $this->traverseNode($subNode); Chris@0: if ($this->stopTraversal) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($this->visitors as $visitor) { Chris@0: $return = $visitor->leaveNode($subNode); Chris@0: if (self::STOP_TRAVERSAL === $return) { Chris@0: $this->stopTraversal = true; Chris@0: break 2; Chris@0: } else if (null !== $return) { Chris@0: if (is_array($return)) { Chris@0: throw new \LogicException( Chris@0: 'leaveNode() may only return an array ' . Chris@0: 'if the parent structure is an array' Chris@0: ); Chris@0: } Chris@0: $subNode = $return; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $node; Chris@0: } Chris@0: Chris@0: protected function traverseArray(array $nodes) { Chris@0: $doNodes = array(); Chris@0: Chris@0: foreach ($nodes as $i => &$node) { Chris@0: if (is_array($node)) { Chris@0: $node = $this->traverseArray($node); Chris@0: if ($this->stopTraversal) { Chris@0: break; Chris@0: } Chris@0: } elseif ($node instanceof Node) { Chris@0: $traverseChildren = true; Chris@0: foreach ($this->visitors as $visitor) { Chris@0: $return = $visitor->enterNode($node); Chris@0: if (self::DONT_TRAVERSE_CHILDREN === $return) { Chris@0: $traverseChildren = false; Chris@0: } else if (self::STOP_TRAVERSAL === $return) { Chris@0: $this->stopTraversal = true; Chris@0: break 2; Chris@0: } else if (null !== $return) { Chris@0: $node = $return; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($traverseChildren) { Chris@0: $node = $this->traverseNode($node); Chris@0: if ($this->stopTraversal) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($this->visitors as $visitor) { Chris@0: $return = $visitor->leaveNode($node); Chris@0: Chris@0: if (self::REMOVE_NODE === $return) { Chris@0: $doNodes[] = array($i, array()); Chris@0: break; Chris@0: } else if (self::STOP_TRAVERSAL === $return) { Chris@0: $this->stopTraversal = true; Chris@0: break 2; Chris@0: } elseif (is_array($return)) { Chris@0: $doNodes[] = array($i, $return); Chris@0: break; Chris@0: } elseif (null !== $return) { Chris@0: $node = $return; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: if (!empty($doNodes)) { Chris@0: while (list($i, $replace) = array_pop($doNodes)) { Chris@0: array_splice($nodes, $i, 1, $replace); Chris@0: } Chris@0: } Chris@0: Chris@0: return $nodes; Chris@0: } Chris@0: }