PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Dependencies / PhpParser / NodeTraverser.php

NodeTraverser.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.5.0, at includes/Dependencies/PhpParser/NodeTraverser.php

205 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser;
4
5 class NodeTraverser implements NodeTraverserInterface
6 {
7 /**
8 * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
9 * of the current node will not be traversed for any visitors.
10 *
11 * For subsequent visitors enterNode() will still be called on the current
12 * node and leaveNode() will also be invoked for the current node.
13 */
14 const DONT_TRAVERSE_CHILDREN = 1;
15
16 /**
17 * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
18 * STOP_TRAVERSAL, traversal is aborted.
19 *
20 * The afterTraverse() method will still be invoked.
21 */
22 const STOP_TRAVERSAL = 2;
23
24 /**
25 * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
26 * in an array, it will be removed from the array.
27 *
28 * For subsequent visitors leaveNode() will still be invoked for the
29 * removed node.
30 */
31 const REMOVE_NODE = false;
32
33 /** @var NodeVisitor[] Visitors */
34 protected $visitors;
35
36 /** @var bool Whether traversal should be stopped */
37 protected $stopTraversal;
38
39 /**
40 * Constructs a node traverser.
41 */
42 public function __construct() {
43 $this->visitors = array();
44 }
45
46 /**
47 * Adds a visitor.
48 *
49 * @param NodeVisitor $visitor Visitor to add
50 */
51 public function addVisitor(NodeVisitor $visitor) {
52 $this->visitors[] = $visitor;
53 }
54
55 /**
56 * Removes an added visitor.
57 *
58 * @param NodeVisitor $visitor
59 */
60 public function removeVisitor(NodeVisitor $visitor) {
61 foreach ($this->visitors as $index => $storedVisitor) {
62 if ($storedVisitor === $visitor) {
63 unset($this->visitors[$index]);
64 break;
65 }
66 }
67 }
68
69 /**
70 * Traverses an array of nodes using the registered visitors.
71 *
72 * @param Node[] $nodes Array of nodes
73 *
74 * @return Node[] Traversed array of nodes
75 */
76 public function traverse(array $nodes) {
77 $this->stopTraversal = false;
78
79 foreach ($this->visitors as $visitor) {
80 if (null !== $return = $visitor->beforeTraverse($nodes)) {
81 $nodes = $return;
82 }
83 }
84
85 $nodes = $this->traverseArray($nodes);
86
87 foreach ($this->visitors as $visitor) {
88 if (null !== $return = $visitor->afterTraverse($nodes)) {
89 $nodes = $return;
90 }
91 }
92
93 return $nodes;
94 }
95
96 protected function traverseNode(Node $node) {
97 foreach ($node->getSubNodeNames() as $name) {
98 $subNode =& $node->$name;
99
100 if (is_array($subNode)) {
101 $subNode = $this->traverseArray($subNode);
102 if ($this->stopTraversal) {
103 break;
104 }
105 } elseif ($subNode instanceof Node) {
106 $traverseChildren = true;
107 foreach ($this->visitors as $visitor) {
108 $return = $visitor->enterNode($subNode);
109 if (self::DONT_TRAVERSE_CHILDREN === $return) {
110 $traverseChildren = false;
111 } else if (self::STOP_TRAVERSAL === $return) {
112 $this->stopTraversal = true;
113 break 2;
114 } else if (null !== $return) {
115 $subNode = $return;
116 }
117 }
118
119 if ($traverseChildren) {
120 $subNode = $this->traverseNode($subNode);
121 if ($this->stopTraversal) {
122 break;
123 }
124 }
125
126 foreach ($this->visitors as $visitor) {
127 $return = $visitor->leaveNode($subNode);
128 if (self::STOP_TRAVERSAL === $return) {
129 $this->stopTraversal = true;
130 break 2;
131 } else if (null !== $return) {
132 if (is_array($return)) {
133 throw new \LogicException(
134 'leaveNode() may only return an array ' .
135 'if the parent structure is an array'
136 );
137 }
138 $subNode = $return;
139 }
140 }
141 }
142 }
143
144 return $node;
145 }
146
147 protected function traverseArray(array $nodes) {
148 $doNodes = array();
149
150 foreach ($nodes as $i => &$node) {
151 if (is_array($node)) {
152 $node = $this->traverseArray($node);
153 if ($this->stopTraversal) {
154 break;
155 }
156 } elseif ($node instanceof Node) {
157 $traverseChildren = true;
158 foreach ($this->visitors as $visitor) {
159 $return = $visitor->enterNode($node);
160 if (self::DONT_TRAVERSE_CHILDREN === $return) {
161 $traverseChildren = false;
162 } else if (self::STOP_TRAVERSAL === $return) {
163 $this->stopTraversal = true;
164 break 2;
165 } else if (null !== $return) {
166 $node = $return;
167 }
168 }
169
170 if ($traverseChildren) {
171 $node = $this->traverseNode($node);
172 if ($this->stopTraversal) {
173 break;
174 }
175 }
176
177 foreach ($this->visitors as $visitor) {
178 $return = $visitor->leaveNode($node);
179
180 if (self::REMOVE_NODE === $return) {
181 $doNodes[] = array($i, array());
182 break;
183 } else if (self::STOP_TRAVERSAL === $return) {
184 $this->stopTraversal = true;
185 break 2;
186 } elseif (is_array($return)) {
187 $doNodes[] = array($i, $return);
188 break;
189 } elseif (null !== $return) {
190 $node = $return;
191 }
192 }
193 }
194 }
195
196 if (!empty($doNodes)) {
197 while (list($i, $replace) = array_pop($doNodes)) {
198 array_splice($nodes, $i, 1, $replace);
199 }
200 }
201
202 return $nodes;
203 }
204 }
205