1: <?php declare(strict_types=1);
2:
3: namespace PhpParser\NodeVisitor;
4:
5: use PhpParser\Node;
6: use PhpParser\NodeVisitorAbstract;
7:
8: /**
9: * Visitor that connects a child node to its parent node
10: * as well as its sibling nodes.
11: *
12: * With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
13: * <code>$node->getAttribute('parent')</code>, the previous
14: * node can be accessed through <code>$node->getAttribute('previous')</code>,
15: * and the next node can be accessed through <code>$node->getAttribute('next')</code>.
16: *
17: * With <code>$weakReferences=true</code> attribute names are prefixed by "weak_", e.g. "weak_parent".
18: */
19: final class NodeConnectingVisitor extends NodeVisitorAbstract {
20: /**
21: * @var Node[]
22: */
23: private array $stack = [];
24:
25: /**
26: * @var ?Node
27: */
28: private $previous;
29:
30: private bool $weakReferences;
31:
32: public function __construct(bool $weakReferences = false) {
33: $this->weakReferences = $weakReferences;
34: }
35:
36: public function beforeTraverse(array $nodes) {
37: $this->stack = [];
38: $this->previous = null;
39: }
40:
41: public function enterNode(Node $node) {
42: if (!empty($this->stack)) {
43: $parent = $this->stack[count($this->stack) - 1];
44: if ($this->weakReferences) {
45: $node->setAttribute('weak_parent', \WeakReference::create($parent));
46: } else {
47: $node->setAttribute('parent', $parent);
48: }
49: }
50:
51: if ($this->previous !== null) {
52: if (
53: $this->weakReferences
54: ) {
55: if ($this->previous->getAttribute('weak_parent') === $node->getAttribute('weak_parent')) {
56: $node->setAttribute('weak_previous', \WeakReference::create($this->previous));
57: $this->previous->setAttribute('weak_next', \WeakReference::create($node));
58: }
59: } elseif ($this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
60: $node->setAttribute('previous', $this->previous);
61: $this->previous->setAttribute('next', $node);
62: }
63: }
64:
65: $this->stack[] = $node;
66: }
67:
68: public function leaveNode(Node $node) {
69: $this->previous = $node;
70:
71: array_pop($this->stack);
72: }
73: }
74: