*/
const REMOVE_NODE = 3;
+ /**
+ * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
+ * of the current node will not be traversed for any visitors.
+ *
+ * For subsequent visitors enterNode() will not be called as well.
+ * leaveNode() will be invoked for visitors that has enterNode() method invoked.
+ */
+ const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
+
/** @var NodeVisitor[] Visitors */
- protected $visitors;
+ protected $visitors = [];
/** @var bool Whether traversal should be stopped */
protected $stopTraversal;
- /**
- * Constructs a node traverser.
- */
public function __construct() {
- $this->visitors = [];
+ // for BC
}
/**
}
} elseif ($subNode instanceof Node) {
$traverseChildren = true;
- foreach ($this->visitors as $visitor) {
+ $breakVisitorIndex = null;
+
+ foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$subNode = $return;
} elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
+ } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
+ $traverseChildren = false;
+ $breakVisitorIndex = $visitorIndex;
+ break;
} elseif (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
}
}
- foreach ($this->visitors as $visitor) {
+ foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($subNode);
+
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
);
}
}
+
+ if ($breakVisitorIndex === $visitorIndex) {
+ break;
+ }
}
}
}
foreach ($nodes as $i => &$node) {
if ($node instanceof Node) {
$traverseChildren = true;
- foreach ($this->visitors as $visitor) {
+ $breakVisitorIndex = null;
+
+ foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$node = $return;
} elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
+ } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
+ $traverseChildren = false;
+ $breakVisitorIndex = $visitorIndex;
+ break;
} elseif (self::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
}
}
- foreach ($this->visitors as $visitor) {
+ foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->leaveNode($node);
+
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
);
}
}
+
+ if ($breakVisitorIndex === $visitorIndex) {
+ break;
+ }
}
} elseif (\is_array($node)) {
throw new \LogicException('Invalid node structure: Contains nested arrays');