PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.5.1
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.5.1
0.5.7 0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 All 33 releases
code-engine / vendor / nikic / php-parser / lib / PhpParser / NodeTraverser.php

NodeTraverser.php in Code Engine – PHP Snippets, AI Functions & Automation for WordPress 0.5.1, at vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php

279 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types=1);
2
3 namespace PhpParser;
4
5 class NodeTraverser implements NodeTraverserInterface {
6 /**
7 * @deprecated Use NodeVisitor::DONT_TRAVERSE_CHILDREN instead.
8 */
9 public const DONT_TRAVERSE_CHILDREN = NodeVisitor::DONT_TRAVERSE_CHILDREN;
10
11 /**
12 * @deprecated Use NodeVisitor::STOP_TRAVERSAL instead.
13 */
14 public const STOP_TRAVERSAL = NodeVisitor::STOP_TRAVERSAL;
15
16 /**
17 * @deprecated Use NodeVisitor::REMOVE_NODE instead.
18 */
19 public const REMOVE_NODE = NodeVisitor::REMOVE_NODE;
20
21 /**
22 * @deprecated Use NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN instead.
23 */
24 public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
25
26 /** @var list<NodeVisitor> Visitors */
27 protected array $visitors = [];
28
29 /** @var bool Whether traversal should be stopped */
30 protected bool $stopTraversal;
31
32 /**
33 * Create a traverser with the given visitors.
34 *
35 * @param NodeVisitor ...$visitors Node visitors
36 */
37 public function __construct(NodeVisitor ...$visitors) {
38 $this->visitors = $visitors;
39 }
40
41 /**
42 * Adds a visitor.
43 *
44 * @param NodeVisitor $visitor Visitor to add
45 */
46 public function addVisitor(NodeVisitor $visitor): void {
47 $this->visitors[] = $visitor;
48 }
49
50 /**
51 * Removes an added visitor.
52 */
53 public function removeVisitor(NodeVisitor $visitor): void {
54 $index = array_search($visitor, $this->visitors);
55 if ($index !== false) {
56 array_splice($this->visitors, $index, 1, []);
57 }
58 }
59
60 /**
61 * Traverses an array of nodes using the registered visitors.
62 *
63 * @param Node[] $nodes Array of nodes
64 *
65 * @return Node[] Traversed array of nodes
66 */
67 public function traverse(array $nodes): array {
68 $this->stopTraversal = false;
69
70 foreach ($this->visitors as $visitor) {
71 if (null !== $return = $visitor->beforeTraverse($nodes)) {
72 $nodes = $return;
73 }
74 }
75
76 $nodes = $this->traverseArray($nodes);
77
78 for ($i = \count($this->visitors) - 1; $i >= 0; --$i) {
79 $visitor = $this->visitors[$i];
80 if (null !== $return = $visitor->afterTraverse($nodes)) {
81 $nodes = $return;
82 }
83 }
84
85 return $nodes;
86 }
87
88 /**
89 * Recursively traverse a node.
90 *
91 * @param Node $node Node to traverse.
92 */
93 protected function traverseNode(Node $node): void {
94 foreach ($node->getSubNodeNames() as $name) {
95 $subNode = $node->$name;
96
97 if (\is_array($subNode)) {
98 $node->$name = $this->traverseArray($subNode);
99 if ($this->stopTraversal) {
100 break;
101 }
102 } elseif ($subNode instanceof Node) {
103 $traverseChildren = true;
104 $visitorIndex = -1;
105
106 foreach ($this->visitors as $visitorIndex => $visitor) {
107 $return = $visitor->enterNode($subNode);
108 if (null !== $return) {
109 if ($return instanceof Node) {
110 $this->ensureReplacementReasonable($subNode, $return);
111 $subNode = $node->$name = $return;
112 } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
113 $traverseChildren = false;
114 } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
115 $traverseChildren = false;
116 break;
117 } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
118 $this->stopTraversal = true;
119 break 2;
120 } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
121 $node->$name = null;
122 continue 2;
123 } else {
124 throw new \LogicException(
125 'enterNode() returned invalid value of type ' . gettype($return)
126 );
127 }
128 }
129 }
130
131 if ($traverseChildren) {
132 $this->traverseNode($subNode);
133 if ($this->stopTraversal) {
134 break;
135 }
136 }
137
138 for (; $visitorIndex >= 0; --$visitorIndex) {
139 $visitor = $this->visitors[$visitorIndex];
140 $return = $visitor->leaveNode($subNode);
141
142 if (null !== $return) {
143 if ($return instanceof Node) {
144 $this->ensureReplacementReasonable($subNode, $return);
145 $subNode = $node->$name = $return;
146 } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
147 $this->stopTraversal = true;
148 break 2;
149 } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
150 $node->$name = null;
151 break;
152 } elseif (\is_array($return)) {
153 throw new \LogicException(
154 'leaveNode() may only return an array ' .
155 'if the parent structure is an array'
156 );
157 } else {
158 throw new \LogicException(
159 'leaveNode() returned invalid value of type ' . gettype($return)
160 );
161 }
162 }
163 }
164 }
165 }
166 }
167
168 /**
169 * Recursively traverse array (usually of nodes).
170 *
171 * @param array $nodes Array to traverse
172 *
173 * @return array Result of traversal (may be original array or changed one)
174 */
175 protected function traverseArray(array $nodes): array {
176 $doNodes = [];
177
178 foreach ($nodes as $i => $node) {
179 if ($node instanceof Node) {
180 $traverseChildren = true;
181 $visitorIndex = -1;
182
183 foreach ($this->visitors as $visitorIndex => $visitor) {
184 $return = $visitor->enterNode($node);
185 if (null !== $return) {
186 if ($return instanceof Node) {
187 $this->ensureReplacementReasonable($node, $return);
188 $nodes[$i] = $node = $return;
189 } elseif (\is_array($return)) {
190 $doNodes[] = [$i, $return];
191 continue 2;
192 } elseif (NodeVisitor::REMOVE_NODE === $return) {
193 $doNodes[] = [$i, []];
194 continue 2;
195 } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
196 $traverseChildren = false;
197 } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
198 $traverseChildren = false;
199 break;
200 } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
201 $this->stopTraversal = true;
202 break 2;
203 } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
204 throw new \LogicException(
205 'REPLACE_WITH_NULL can not be used if the parent structure is an array');
206 } else {
207 throw new \LogicException(
208 'enterNode() returned invalid value of type ' . gettype($return)
209 );
210 }
211 }
212 }
213
214 if ($traverseChildren) {
215 $this->traverseNode($node);
216 if ($this->stopTraversal) {
217 break;
218 }
219 }
220
221 for (; $visitorIndex >= 0; --$visitorIndex) {
222 $visitor = $this->visitors[$visitorIndex];
223 $return = $visitor->leaveNode($node);
224
225 if (null !== $return) {
226 if ($return instanceof Node) {
227 $this->ensureReplacementReasonable($node, $return);
228 $nodes[$i] = $node = $return;
229 } elseif (\is_array($return)) {
230 $doNodes[] = [$i, $return];
231 break;
232 } elseif (NodeVisitor::REMOVE_NODE === $return) {
233 $doNodes[] = [$i, []];
234 break;
235 } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
236 $this->stopTraversal = true;
237 break 2;
238 } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
239 throw new \LogicException(
240 'REPLACE_WITH_NULL can not be used if the parent structure is an array');
241 } else {
242 throw new \LogicException(
243 'leaveNode() returned invalid value of type ' . gettype($return)
244 );
245 }
246 }
247 }
248 } elseif (\is_array($node)) {
249 throw new \LogicException('Invalid node structure: Contains nested arrays');
250 }
251 }
252
253 if (!empty($doNodes)) {
254 while (list($i, $replace) = array_pop($doNodes)) {
255 array_splice($nodes, $i, 1, $replace);
256 }
257 }
258
259 return $nodes;
260 }
261
262 private function ensureReplacementReasonable(Node $old, Node $new): void {
263 if ($old instanceof Node\Stmt && $new instanceof Node\Expr) {
264 throw new \LogicException(
265 "Trying to replace statement ({$old->getType()}) " .
266 "with expression ({$new->getType()}). Are you missing a " .
267 "Stmt_Expression wrapper?"
268 );
269 }
270
271 if ($old instanceof Node\Expr && $new instanceof Node\Stmt) {
272 throw new \LogicException(
273 "Trying to replace expression ({$old->getType()}) " .
274 "with statement ({$new->getType()})"
275 );
276 }
277 }
278 }
279