Security update for Core, with self-updated composer
[yaffs-website] / vendor / nikic / php-parser / lib / PhpParser / NodeTraverser.php
1 <?php
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 = 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 }