NodeTraverser.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. namespace PhpParser;
  3. class NodeTraverser implements NodeTraverserInterface
  4. {
  5. /**
  6. * @var NodeVisitor[] Visitors
  7. */
  8. protected $visitors;
  9. /**
  10. * @var bool
  11. */
  12. private $cloneNodes;
  13. /**
  14. * Constructs a node traverser.
  15. *
  16. * @param bool $cloneNodes Should the traverser clone the nodes when traversing the AST
  17. */
  18. public function __construct($cloneNodes = true) {
  19. $this->visitors = array();
  20. $this->cloneNodes = $cloneNodes;
  21. }
  22. /**
  23. * Adds a visitor.
  24. *
  25. * @param NodeVisitor $visitor Visitor to add
  26. */
  27. public function addVisitor(NodeVisitor $visitor) {
  28. $this->visitors[] = $visitor;
  29. }
  30. /**
  31. * Removes an added visitor.
  32. *
  33. * @param NodeVisitor $visitor
  34. */
  35. public function removeVisitor(NodeVisitor $visitor) {
  36. foreach ($this->visitors as $index => $storedVisitor) {
  37. if ($storedVisitor === $visitor) {
  38. unset($this->visitors[$index]);
  39. break;
  40. }
  41. }
  42. }
  43. /**
  44. * Traverses an array of nodes using the registered visitors.
  45. *
  46. * @param Node[] $nodes Array of nodes
  47. *
  48. * @return Node[] Traversed array of nodes
  49. */
  50. public function traverse(array $nodes) {
  51. foreach ($this->visitors as $visitor) {
  52. if (null !== $return = $visitor->beforeTraverse($nodes)) {
  53. $nodes = $return;
  54. }
  55. }
  56. $nodes = $this->traverseArray($nodes);
  57. foreach ($this->visitors as $visitor) {
  58. if (null !== $return = $visitor->afterTraverse($nodes)) {
  59. $nodes = $return;
  60. }
  61. }
  62. return $nodes;
  63. }
  64. protected function traverseNode(Node $node) {
  65. if ($this->cloneNodes) {
  66. $node = clone $node;
  67. }
  68. foreach ($node->getSubNodeNames() as $name) {
  69. $subNode =& $node->$name;
  70. if (is_array($subNode)) {
  71. $subNode = $this->traverseArray($subNode);
  72. } elseif ($subNode instanceof Node) {
  73. $traverseChildren = true;
  74. foreach ($this->visitors as $visitor) {
  75. $return = $visitor->enterNode($subNode);
  76. if (self::DONT_TRAVERSE_CHILDREN === $return) {
  77. $traverseChildren = false;
  78. } else if (null !== $return) {
  79. $subNode = $return;
  80. }
  81. }
  82. if ($traverseChildren) {
  83. $subNode = $this->traverseNode($subNode);
  84. }
  85. foreach ($this->visitors as $visitor) {
  86. if (null !== $return = $visitor->leaveNode($subNode)) {
  87. $subNode = $return;
  88. }
  89. }
  90. }
  91. }
  92. return $node;
  93. }
  94. protected function traverseArray(array $nodes) {
  95. $doNodes = array();
  96. foreach ($nodes as $i => &$node) {
  97. if (is_array($node)) {
  98. $node = $this->traverseArray($node);
  99. } elseif ($node instanceof Node) {
  100. $traverseChildren = true;
  101. foreach ($this->visitors as $visitor) {
  102. $return = $visitor->enterNode($node);
  103. if (self::DONT_TRAVERSE_CHILDREN === $return) {
  104. $traverseChildren = false;
  105. } else if (null !== $return) {
  106. $node = $return;
  107. }
  108. }
  109. if ($traverseChildren) {
  110. $node = $this->traverseNode($node);
  111. }
  112. foreach ($this->visitors as $visitor) {
  113. $return = $visitor->leaveNode($node);
  114. if (self::REMOVE_NODE === $return) {
  115. $doNodes[] = array($i, array());
  116. break;
  117. } elseif (is_array($return)) {
  118. $doNodes[] = array($i, $return);
  119. break;
  120. } elseif (null !== $return) {
  121. $node = $return;
  122. }
  123. }
  124. }
  125. }
  126. if (!empty($doNodes)) {
  127. while (list($i, $replace) = array_pop($doNodes)) {
  128. array_splice($nodes, $i, 1, $replace);
  129. }
  130. }
  131. return $nodes;
  132. }
  133. }