Visitors */ private $visitors = []; /** @var bool Whether traversal should be stopped */ private $stopTraversal; /** * @param list $visitors */ public function __construct(array $visitors) { $this->visitors = $visitors; } /** * Traverses an array of nodes using the registered visitors. * * @param Node[] $nodes Array of nodes * * @return Node[] Traversed array of nodes */ public function traverse(array $nodes): array { $this->stopTraversal = false; foreach ($this->visitors as $visitor) { $return = $visitor->beforeTraverse($nodes); if ($return === null) { continue; } $nodes = $return; } $nodes = $this->traverseArray($nodes); foreach ($this->visitors as $visitor) { $return = $visitor->afterTraverse($nodes); if ($return === null) { continue; } $nodes = $return; } return $nodes; } /** * Recursively traverse a node. * * @param Node $node Node to traverse. * * @return Node Result of traversal (may be original node or new one) */ private function traverseNode(Node $node): Node { $subNodeNames = array_keys(get_object_vars($node)); foreach ($subNodeNames as $name) { $subNode =& $node->$name; if (is_array($subNode)) { $subNode = $this->traverseArray($subNode); if ($this->stopTraversal) { break; } } elseif ($subNode instanceof Node) { $traverseChildren = true; $breakVisitorIndex = null; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($subNode); if ($return === null) { continue; } if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); $subNode = $return; } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { $traverseChildren = false; } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { $traverseChildren = false; $breakVisitorIndex = $visitorIndex; break; } elseif ($return === self::STOP_TRAVERSAL) { $this->stopTraversal = true; break 2; } else { throw new LogicException( 'enterNode() returned invalid value of type ' . gettype($return) ); } } if ($traverseChildren) { $subNode = $this->traverseNode($subNode); if ($this->stopTraversal) { break; } } foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->leaveNode($subNode); if ($return !== null) { if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); $subNode = $return; } elseif ($return === self::STOP_TRAVERSAL) { $this->stopTraversal = true; break 2; } 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) ); } } if ($breakVisitorIndex === $visitorIndex) { break; } } } } return $node; } /** * Recursively traverse array (usually of nodes). * * @param mixed[] $nodes Array to traverse * * @return mixed[] Result of traversal (may be original array or changed one) */ private function traverseArray(array $nodes): array { $doNodes = []; foreach ($nodes as $i => &$node) { if ($node instanceof Node) { $traverseChildren = true; $breakVisitorIndex = null; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($node); if ($return === null) { continue; } if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); $node = $return; } elseif (is_array($return)) { $doNodes[] = [$i, $return]; continue 2; } elseif ($return === self::REMOVE_NODE) { $doNodes[] = [$i, []]; continue 2; } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { $traverseChildren = false; } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { $traverseChildren = false; $breakVisitorIndex = $visitorIndex; break; } elseif ($return === self::STOP_TRAVERSAL) { $this->stopTraversal = true; break 2; } else { throw new LogicException( 'enterNode() returned invalid value of type ' . gettype($return) ); } } if ($traverseChildren) { $node = $this->traverseNode($node); if ($this->stopTraversal) { break; } } foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->leaveNode($node); if ($return !== null) { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); $node = $return; } elseif (is_array($return)) { $doNodes[] = [$i, $return]; break; } elseif ($return === self::REMOVE_NODE) { $doNodes[] = [$i, []]; break; } elseif ($return === self::STOP_TRAVERSAL) { $this->stopTraversal = true; break 2; } else { throw new LogicException( 'leaveNode() returned invalid value of type ' . gettype($return) ); } } if ($breakVisitorIndex === $visitorIndex) { break; } } } elseif (is_array($node)) { throw new LogicException('Invalid node structure: Contains nested arrays'); } } if (count($doNodes) > 0) { while ([$i, $replace] = array_pop($doNodes)) { array_splice($nodes, $i, 1, $replace); } } return $nodes; } private function ensureReplacementReasonable(Node $old, Node $new): void { if ($old instanceof TypeNode && !$new instanceof TypeNode) { throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new))); } if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) { throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new))); } if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) { throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new))); } if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) { throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new))); } } } __halt_compiler();----SIGNATURE:----dsN8JWPgKV6G/2OX4lcdbM+871pZod48W9VekNy/N/fha+FK/JH2HPPXXTRuEWEld/zO+9hKLfOF77PvKpA3hxrQPFF67cr9XfHSw+JNKkxGS+ENNI+iblnuVOQJCIM1qWfBQxn9tx6vlhoEXRhcX88htRSr01y/z9Fi5p1w4gsMJ43TchO0u/eLEiqLiq0rcX6CVm8NySSrKsjl9PcYLZHPvkwBW6YaSgws4rPBU9hT5cgI4eot2XFu2IAfT/D9RTfpWHM9U1VDNjSLltfJQ9KTK5/tyom3Szt+VInE0kKoxgxmsVUt2gFuPccCpLNNV2hnELdKUlxZI1YdqLzgQTqtO904FbX1PEFTCjJwFLIR/iQbQ7WiS6OpWdm6pVYXsUQJy7TbxAPE9B+NEVyHbRmLtCW6D59C66bMaliSMFVwishIpLnQbitWTIQqfyImHTAUdk2mc2GG+vOU1FpB/LRE59yPXXvCM6vyU3FaMVfJPcHZ8AfbdJwvFCeCe8aiyk4jQUcA2MIhG4APZ3UkH3Dx0L4uI/A/chISvm265+lZPgUyda2V/UK3sQY8A7IgzMXKO3L0EIRvu/kGpj0bXwSh5MEocVfPXQh8dQnGO/Sr2PIJJg2HxlKSvRef2aRK1guNOsjnZxCc/lx8Q8pLoeNoMdsiOhsEmcqfo09TXPI=----ATTACHMENT:----OTAyMTYzMTI3OTc3MDcwMiA4Njk3MDg5MTU4NjcwODAyIDE4NjM2MDQ0MjMxNDk2OTg=