summaryrefslogtreecommitdiff
path: root/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php')
-rw-r--r--vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php291
1 files changed, 291 insertions, 0 deletions
diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php
new file mode 100644
index 000000000..97d45bdaa
--- /dev/null
+++ b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php
@@ -0,0 +1,291 @@
+<?php declare(strict_types=1);
+
+namespace PhpParser;
+
+class NodeTraverser implements NodeTraverserInterface
+{
+ /**
+ * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
+ * of the current node will not be traversed for any visitors.
+ *
+ * For subsequent visitors enterNode() will still be called on the current
+ * node and leaveNode() will also be invoked for the current node.
+ */
+ const DONT_TRAVERSE_CHILDREN = 1;
+
+ /**
+ * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
+ * STOP_TRAVERSAL, traversal is aborted.
+ *
+ * The afterTraverse() method will still be invoked.
+ */
+ const STOP_TRAVERSAL = 2;
+
+ /**
+ * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
+ * in an array, it will be removed from the array.
+ *
+ * For subsequent visitors leaveNode() will still be invoked for the
+ * removed node.
+ */
+ const REMOVE_NODE = 3;
+
+ /**
+ * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
+ * of the current node will not be traversed for any visitors.
+ *
+ * For subsequent visitors enterNode() will not be called as well.
+ * leaveNode() will be invoked for visitors that has enterNode() method invoked.
+ */
+ const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
+
+ /** @var NodeVisitor[] Visitors */
+ protected $visitors = [];
+
+ /** @var bool Whether traversal should be stopped */
+ protected $stopTraversal;
+
+ public function __construct() {
+ // for BC
+ }
+
+ /**
+ * Adds a visitor.
+ *
+ * @param NodeVisitor $visitor Visitor to add
+ */
+ public function addVisitor(NodeVisitor $visitor) {
+ $this->visitors[] = $visitor;
+ }
+
+ /**
+ * Removes an added visitor.
+ *
+ * @param NodeVisitor $visitor
+ */
+ public function removeVisitor(NodeVisitor $visitor) {
+ foreach ($this->visitors as $index => $storedVisitor) {
+ if ($storedVisitor === $visitor) {
+ unset($this->visitors[$index]);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ if (null !== $return = $visitor->beforeTraverse($nodes)) {
+ $nodes = $return;
+ }
+ }
+
+ $nodes = $this->traverseArray($nodes);
+
+ foreach ($this->visitors as $visitor) {
+ if (null !== $return = $visitor->afterTraverse($nodes)) {
+ $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)
+ */
+ protected function traverseNode(Node $node) : Node {
+ foreach ($node->getSubNodeNames() 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 (null !== $return) {
+ if ($return instanceof Node) {
+ $this->ensureReplacementReasonable($subNode, $return);
+ $subNode = $return;
+ } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
+ $traverseChildren = false;
+ } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
+ $traverseChildren = false;
+ $breakVisitorIndex = $visitorIndex;
+ break;
+ } elseif (self::STOP_TRAVERSAL === $return) {
+ $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 (null !== $return) {
+ if ($return instanceof Node) {
+ $this->ensureReplacementReasonable($subNode, $return);
+ $subNode = $return;
+ } elseif (self::STOP_TRAVERSAL === $return) {
+ $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 array $nodes Array to traverse
+ *
+ * @return array Result of traversal (may be original array or changed one)
+ */
+ protected 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 (null !== $return) {
+ if ($return instanceof Node) {
+ $this->ensureReplacementReasonable($node, $return);
+ $node = $return;
+ } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
+ $traverseChildren = false;
+ } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
+ $traverseChildren = false;
+ $breakVisitorIndex = $visitorIndex;
+ break;
+ } elseif (self::STOP_TRAVERSAL === $return) {
+ $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 (null !== $return) {
+ if ($return instanceof Node) {
+ $this->ensureReplacementReasonable($node, $return);
+ $node = $return;
+ } elseif (\is_array($return)) {
+ $doNodes[] = [$i, $return];
+ break;
+ } elseif (self::REMOVE_NODE === $return) {
+ $doNodes[] = [$i, []];
+ break;
+ } elseif (self::STOP_TRAVERSAL === $return) {
+ $this->stopTraversal = true;
+ break 2;
+ } elseif (false === $return) {
+ throw new \LogicException(
+ 'bool(false) return from leaveNode() no longer supported. ' .
+ 'Return NodeTraverser::REMOVE_NODE instead'
+ );
+ } 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 (!empty($doNodes)) {
+ while (list($i, $replace) = array_pop($doNodes)) {
+ array_splice($nodes, $i, 1, $replace);
+ }
+ }
+
+ return $nodes;
+ }
+
+ private function ensureReplacementReasonable($old, $new) {
+ if ($old instanceof Node\Stmt && $new instanceof Node\Expr) {
+ throw new \LogicException(
+ "Trying to replace statement ({$old->getType()}) " .
+ "with expression ({$new->getType()}). Are you missing a " .
+ "Stmt_Expression wrapper?"
+ );
+ }
+
+ if ($old instanceof Node\Expr && $new instanceof Node\Stmt) {
+ throw new \LogicException(
+ "Trying to replace expression ({$old->getType()}) " .
+ "with statement ({$new->getType()})"
+ );
+ }
+ }
+}