Mercurial > hg > cmmr2012-drupal-site
comparison vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php declare(strict_types=1); | |
2 | |
3 namespace 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 = 3; | |
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 = []; | |
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) : array { | |
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 /** | |
97 * Recursively traverse a node. | |
98 * | |
99 * @param Node $node Node to traverse. | |
100 * | |
101 * @return Node Result of traversal (may be original node or new one) | |
102 */ | |
103 protected function traverseNode(Node $node) : Node { | |
104 foreach ($node->getSubNodeNames() as $name) { | |
105 $subNode =& $node->$name; | |
106 | |
107 if (\is_array($subNode)) { | |
108 $subNode = $this->traverseArray($subNode); | |
109 if ($this->stopTraversal) { | |
110 break; | |
111 } | |
112 } elseif ($subNode instanceof Node) { | |
113 $traverseChildren = true; | |
114 foreach ($this->visitors as $visitor) { | |
115 $return = $visitor->enterNode($subNode); | |
116 if (null !== $return) { | |
117 if ($return instanceof Node) { | |
118 $this->ensureReplacementReasonable($subNode, $return); | |
119 $subNode = $return; | |
120 } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { | |
121 $traverseChildren = false; | |
122 } elseif (self::STOP_TRAVERSAL === $return) { | |
123 $this->stopTraversal = true; | |
124 break 2; | |
125 } else { | |
126 throw new \LogicException( | |
127 'enterNode() returned invalid value of type ' . gettype($return) | |
128 ); | |
129 } | |
130 } | |
131 } | |
132 | |
133 if ($traverseChildren) { | |
134 $subNode = $this->traverseNode($subNode); | |
135 if ($this->stopTraversal) { | |
136 break; | |
137 } | |
138 } | |
139 | |
140 foreach ($this->visitors as $visitor) { | |
141 $return = $visitor->leaveNode($subNode); | |
142 if (null !== $return) { | |
143 if ($return instanceof Node) { | |
144 $this->ensureReplacementReasonable($subNode, $return); | |
145 $subNode = $return; | |
146 } elseif (self::STOP_TRAVERSAL === $return) { | |
147 $this->stopTraversal = true; | |
148 break 2; | |
149 } elseif (\is_array($return)) { | |
150 throw new \LogicException( | |
151 'leaveNode() may only return an array ' . | |
152 'if the parent structure is an array' | |
153 ); | |
154 } else { | |
155 throw new \LogicException( | |
156 'leaveNode() returned invalid value of type ' . gettype($return) | |
157 ); | |
158 } | |
159 } | |
160 } | |
161 } | |
162 } | |
163 | |
164 return $node; | |
165 } | |
166 | |
167 /** | |
168 * Recursively traverse array (usually of nodes). | |
169 * | |
170 * @param array $nodes Array to traverse | |
171 * | |
172 * @return array Result of traversal (may be original array or changed one) | |
173 */ | |
174 protected function traverseArray(array $nodes) : array { | |
175 $doNodes = []; | |
176 | |
177 foreach ($nodes as $i => &$node) { | |
178 if ($node instanceof Node) { | |
179 $traverseChildren = true; | |
180 foreach ($this->visitors as $visitor) { | |
181 $return = $visitor->enterNode($node); | |
182 if (null !== $return) { | |
183 if ($return instanceof Node) { | |
184 $this->ensureReplacementReasonable($node, $return); | |
185 $node = $return; | |
186 } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { | |
187 $traverseChildren = false; | |
188 } elseif (self::STOP_TRAVERSAL === $return) { | |
189 $this->stopTraversal = true; | |
190 break 2; | |
191 } else { | |
192 throw new \LogicException( | |
193 'enterNode() returned invalid value of type ' . gettype($return) | |
194 ); | |
195 } | |
196 } | |
197 } | |
198 | |
199 if ($traverseChildren) { | |
200 $node = $this->traverseNode($node); | |
201 if ($this->stopTraversal) { | |
202 break; | |
203 } | |
204 } | |
205 | |
206 foreach ($this->visitors as $visitor) { | |
207 $return = $visitor->leaveNode($node); | |
208 if (null !== $return) { | |
209 if ($return instanceof Node) { | |
210 $this->ensureReplacementReasonable($node, $return); | |
211 $node = $return; | |
212 } elseif (\is_array($return)) { | |
213 $doNodes[] = [$i, $return]; | |
214 break; | |
215 } elseif (self::REMOVE_NODE === $return) { | |
216 $doNodes[] = [$i, []]; | |
217 break; | |
218 } elseif (self::STOP_TRAVERSAL === $return) { | |
219 $this->stopTraversal = true; | |
220 break 2; | |
221 } elseif (false === $return) { | |
222 throw new \LogicException( | |
223 'bool(false) return from leaveNode() no longer supported. ' . | |
224 'Return NodeTraverser::REMOVE_NODE instead' | |
225 ); | |
226 } else { | |
227 throw new \LogicException( | |
228 'leaveNode() returned invalid value of type ' . gettype($return) | |
229 ); | |
230 } | |
231 } | |
232 } | |
233 } elseif (\is_array($node)) { | |
234 throw new \LogicException('Invalid node structure: Contains nested arrays'); | |
235 } | |
236 } | |
237 | |
238 if (!empty($doNodes)) { | |
239 while (list($i, $replace) = array_pop($doNodes)) { | |
240 array_splice($nodes, $i, 1, $replace); | |
241 } | |
242 } | |
243 | |
244 return $nodes; | |
245 } | |
246 | |
247 private function ensureReplacementReasonable($old, $new) { | |
248 if ($old instanceof Node\Stmt && $new instanceof Node\Expr) { | |
249 throw new \LogicException( | |
250 "Trying to replace statement ({$old->getType()}) " . | |
251 "with expression ({$new->getType()}). Are you missing a " . | |
252 "Stmt_Expression wrapper?" | |
253 ); | |
254 } | |
255 | |
256 if ($old instanceof Node\Expr && $new instanceof Node\Stmt) { | |
257 throw new \LogicException( | |
258 "Trying to replace expression ({$old->getType()}) " . | |
259 "with statement ({$new->getType()})" | |
260 ); | |
261 } | |
262 } | |
263 } |