NodeConnectingVisitor.php 2.3 KB

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