annotate vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php @ 13:5fb285c0d0e3

Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've been lucky to get away with this so far, as we don't support self-registration which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5 was vulnerable to.
author Chris Cannam
date Mon, 23 Apr 2018 09:33:26 +0100
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@13 1 <?php declare(strict_types=1);
Chris@0 2
Chris@0 3 namespace PhpParser;
Chris@0 4
Chris@0 5 class NodeTraverser implements NodeTraverserInterface
Chris@0 6 {
Chris@0 7 /**
Chris@0 8 * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
Chris@0 9 * of the current node will not be traversed for any visitors.
Chris@0 10 *
Chris@0 11 * For subsequent visitors enterNode() will still be called on the current
Chris@0 12 * node and leaveNode() will also be invoked for the current node.
Chris@0 13 */
Chris@0 14 const DONT_TRAVERSE_CHILDREN = 1;
Chris@0 15
Chris@0 16 /**
Chris@0 17 * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
Chris@0 18 * STOP_TRAVERSAL, traversal is aborted.
Chris@0 19 *
Chris@0 20 * The afterTraverse() method will still be invoked.
Chris@0 21 */
Chris@0 22 const STOP_TRAVERSAL = 2;
Chris@0 23
Chris@0 24 /**
Chris@0 25 * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
Chris@0 26 * in an array, it will be removed from the array.
Chris@0 27 *
Chris@0 28 * For subsequent visitors leaveNode() will still be invoked for the
Chris@0 29 * removed node.
Chris@0 30 */
Chris@13 31 const REMOVE_NODE = 3;
Chris@0 32
Chris@0 33 /** @var NodeVisitor[] Visitors */
Chris@0 34 protected $visitors;
Chris@0 35
Chris@0 36 /** @var bool Whether traversal should be stopped */
Chris@0 37 protected $stopTraversal;
Chris@0 38
Chris@0 39 /**
Chris@0 40 * Constructs a node traverser.
Chris@0 41 */
Chris@0 42 public function __construct() {
Chris@13 43 $this->visitors = [];
Chris@0 44 }
Chris@0 45
Chris@0 46 /**
Chris@0 47 * Adds a visitor.
Chris@0 48 *
Chris@0 49 * @param NodeVisitor $visitor Visitor to add
Chris@0 50 */
Chris@0 51 public function addVisitor(NodeVisitor $visitor) {
Chris@0 52 $this->visitors[] = $visitor;
Chris@0 53 }
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Removes an added visitor.
Chris@0 57 *
Chris@0 58 * @param NodeVisitor $visitor
Chris@0 59 */
Chris@0 60 public function removeVisitor(NodeVisitor $visitor) {
Chris@0 61 foreach ($this->visitors as $index => $storedVisitor) {
Chris@0 62 if ($storedVisitor === $visitor) {
Chris@0 63 unset($this->visitors[$index]);
Chris@0 64 break;
Chris@0 65 }
Chris@0 66 }
Chris@0 67 }
Chris@0 68
Chris@0 69 /**
Chris@0 70 * Traverses an array of nodes using the registered visitors.
Chris@0 71 *
Chris@0 72 * @param Node[] $nodes Array of nodes
Chris@0 73 *
Chris@0 74 * @return Node[] Traversed array of nodes
Chris@0 75 */
Chris@13 76 public function traverse(array $nodes) : array {
Chris@0 77 $this->stopTraversal = false;
Chris@0 78
Chris@0 79 foreach ($this->visitors as $visitor) {
Chris@0 80 if (null !== $return = $visitor->beforeTraverse($nodes)) {
Chris@0 81 $nodes = $return;
Chris@0 82 }
Chris@0 83 }
Chris@0 84
Chris@0 85 $nodes = $this->traverseArray($nodes);
Chris@0 86
Chris@0 87 foreach ($this->visitors as $visitor) {
Chris@0 88 if (null !== $return = $visitor->afterTraverse($nodes)) {
Chris@0 89 $nodes = $return;
Chris@0 90 }
Chris@0 91 }
Chris@0 92
Chris@0 93 return $nodes;
Chris@0 94 }
Chris@0 95
Chris@13 96 /**
Chris@13 97 * Recursively traverse a node.
Chris@13 98 *
Chris@13 99 * @param Node $node Node to traverse.
Chris@13 100 *
Chris@13 101 * @return Node Result of traversal (may be original node or new one)
Chris@13 102 */
Chris@13 103 protected function traverseNode(Node $node) : Node {
Chris@0 104 foreach ($node->getSubNodeNames() as $name) {
Chris@0 105 $subNode =& $node->$name;
Chris@0 106
Chris@13 107 if (\is_array($subNode)) {
Chris@0 108 $subNode = $this->traverseArray($subNode);
Chris@0 109 if ($this->stopTraversal) {
Chris@0 110 break;
Chris@0 111 }
Chris@0 112 } elseif ($subNode instanceof Node) {
Chris@0 113 $traverseChildren = true;
Chris@0 114 foreach ($this->visitors as $visitor) {
Chris@0 115 $return = $visitor->enterNode($subNode);
Chris@13 116 if (null !== $return) {
Chris@13 117 if ($return instanceof Node) {
Chris@13 118 $this->ensureReplacementReasonable($subNode, $return);
Chris@13 119 $subNode = $return;
Chris@13 120 } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
Chris@13 121 $traverseChildren = false;
Chris@13 122 } elseif (self::STOP_TRAVERSAL === $return) {
Chris@13 123 $this->stopTraversal = true;
Chris@13 124 break 2;
Chris@13 125 } else {
Chris@13 126 throw new \LogicException(
Chris@13 127 'enterNode() returned invalid value of type ' . gettype($return)
Chris@13 128 );
Chris@13 129 }
Chris@0 130 }
Chris@0 131 }
Chris@0 132
Chris@0 133 if ($traverseChildren) {
Chris@0 134 $subNode = $this->traverseNode($subNode);
Chris@0 135 if ($this->stopTraversal) {
Chris@0 136 break;
Chris@0 137 }
Chris@0 138 }
Chris@0 139
Chris@0 140 foreach ($this->visitors as $visitor) {
Chris@0 141 $return = $visitor->leaveNode($subNode);
Chris@13 142 if (null !== $return) {
Chris@13 143 if ($return instanceof Node) {
Chris@13 144 $this->ensureReplacementReasonable($subNode, $return);
Chris@13 145 $subNode = $return;
Chris@13 146 } elseif (self::STOP_TRAVERSAL === $return) {
Chris@13 147 $this->stopTraversal = true;
Chris@13 148 break 2;
Chris@13 149 } elseif (\is_array($return)) {
Chris@0 150 throw new \LogicException(
Chris@0 151 'leaveNode() may only return an array ' .
Chris@0 152 'if the parent structure is an array'
Chris@0 153 );
Chris@13 154 } else {
Chris@13 155 throw new \LogicException(
Chris@13 156 'leaveNode() returned invalid value of type ' . gettype($return)
Chris@13 157 );
Chris@0 158 }
Chris@0 159 }
Chris@0 160 }
Chris@0 161 }
Chris@0 162 }
Chris@0 163
Chris@0 164 return $node;
Chris@0 165 }
Chris@0 166
Chris@13 167 /**
Chris@13 168 * Recursively traverse array (usually of nodes).
Chris@13 169 *
Chris@13 170 * @param array $nodes Array to traverse
Chris@13 171 *
Chris@13 172 * @return array Result of traversal (may be original array or changed one)
Chris@13 173 */
Chris@13 174 protected function traverseArray(array $nodes) : array {
Chris@13 175 $doNodes = [];
Chris@0 176
Chris@0 177 foreach ($nodes as $i => &$node) {
Chris@13 178 if ($node instanceof Node) {
Chris@0 179 $traverseChildren = true;
Chris@0 180 foreach ($this->visitors as $visitor) {
Chris@0 181 $return = $visitor->enterNode($node);
Chris@13 182 if (null !== $return) {
Chris@13 183 if ($return instanceof Node) {
Chris@13 184 $this->ensureReplacementReasonable($node, $return);
Chris@13 185 $node = $return;
Chris@13 186 } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
Chris@13 187 $traverseChildren = false;
Chris@13 188 } elseif (self::STOP_TRAVERSAL === $return) {
Chris@13 189 $this->stopTraversal = true;
Chris@13 190 break 2;
Chris@13 191 } else {
Chris@13 192 throw new \LogicException(
Chris@13 193 'enterNode() returned invalid value of type ' . gettype($return)
Chris@13 194 );
Chris@13 195 }
Chris@0 196 }
Chris@0 197 }
Chris@0 198
Chris@0 199 if ($traverseChildren) {
Chris@0 200 $node = $this->traverseNode($node);
Chris@0 201 if ($this->stopTraversal) {
Chris@0 202 break;
Chris@0 203 }
Chris@0 204 }
Chris@0 205
Chris@0 206 foreach ($this->visitors as $visitor) {
Chris@0 207 $return = $visitor->leaveNode($node);
Chris@13 208 if (null !== $return) {
Chris@13 209 if ($return instanceof Node) {
Chris@13 210 $this->ensureReplacementReasonable($node, $return);
Chris@13 211 $node = $return;
Chris@13 212 } elseif (\is_array($return)) {
Chris@13 213 $doNodes[] = [$i, $return];
Chris@13 214 break;
Chris@13 215 } elseif (self::REMOVE_NODE === $return) {
Chris@13 216 $doNodes[] = [$i, []];
Chris@13 217 break;
Chris@13 218 } elseif (self::STOP_TRAVERSAL === $return) {
Chris@13 219 $this->stopTraversal = true;
Chris@13 220 break 2;
Chris@13 221 } elseif (false === $return) {
Chris@13 222 throw new \LogicException(
Chris@13 223 'bool(false) return from leaveNode() no longer supported. ' .
Chris@13 224 'Return NodeTraverser::REMOVE_NODE instead'
Chris@13 225 );
Chris@13 226 } else {
Chris@13 227 throw new \LogicException(
Chris@13 228 'leaveNode() returned invalid value of type ' . gettype($return)
Chris@13 229 );
Chris@13 230 }
Chris@0 231 }
Chris@0 232 }
Chris@13 233 } elseif (\is_array($node)) {
Chris@13 234 throw new \LogicException('Invalid node structure: Contains nested arrays');
Chris@0 235 }
Chris@0 236 }
Chris@0 237
Chris@0 238 if (!empty($doNodes)) {
Chris@0 239 while (list($i, $replace) = array_pop($doNodes)) {
Chris@0 240 array_splice($nodes, $i, 1, $replace);
Chris@0 241 }
Chris@0 242 }
Chris@0 243
Chris@0 244 return $nodes;
Chris@0 245 }
Chris@13 246
Chris@13 247 private function ensureReplacementReasonable($old, $new) {
Chris@13 248 if ($old instanceof Node\Stmt && $new instanceof Node\Expr) {
Chris@13 249 throw new \LogicException(
Chris@13 250 "Trying to replace statement ({$old->getType()}) " .
Chris@13 251 "with expression ({$new->getType()}). Are you missing a " .
Chris@13 252 "Stmt_Expression wrapper?"
Chris@13 253 );
Chris@13 254 }
Chris@13 255
Chris@13 256 if ($old instanceof Node\Expr && $new instanceof Node\Stmt) {
Chris@13 257 throw new \LogicException(
Chris@13 258 "Trying to replace expression ({$old->getType()}) " .
Chris@13 259 "with statement ({$new->getType()})"
Chris@13 260 );
Chris@13 261 }
Chris@13 262 }
Chris@0 263 }