NameResolver.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. class PHPParser_NodeVisitor_NameResolver extends PHPParser_NodeVisitorAbstract
  3. {
  4. /**
  5. * @var null|PHPParser_Node_Name Current namespace
  6. */
  7. protected $namespace;
  8. /**
  9. * @var array Currently defined namespace and class aliases
  10. */
  11. protected $aliases;
  12. public function beforeTraverse(array $nodes) {
  13. $this->namespace = null;
  14. $this->aliases = array();
  15. }
  16. public function enterNode(PHPParser_Node $node) {
  17. if ($node instanceof PHPParser_Node_Stmt_Namespace) {
  18. $this->namespace = $node->name;
  19. $this->aliases = array();
  20. } elseif ($node instanceof PHPParser_Node_Stmt_UseUse) {
  21. $aliasName = strtolower($node->alias);
  22. if (isset($this->aliases[$aliasName])) {
  23. throw new PHPParser_Error(
  24. sprintf(
  25. 'Cannot use "%s" as "%s" because the name is already in use',
  26. $node->name, $node->alias
  27. ),
  28. $node->getLine()
  29. );
  30. }
  31. $this->aliases[$aliasName] = $node->name;
  32. } elseif ($node instanceof PHPParser_Node_Stmt_Class) {
  33. if (null !== $node->extends) {
  34. $node->extends = $this->resolveClassName($node->extends);
  35. }
  36. foreach ($node->implements as &$interface) {
  37. $interface = $this->resolveClassName($interface);
  38. }
  39. $this->addNamespacedName($node);
  40. } elseif ($node instanceof PHPParser_Node_Stmt_Interface) {
  41. foreach ($node->extends as &$interface) {
  42. $interface = $this->resolveClassName($interface);
  43. }
  44. $this->addNamespacedName($node);
  45. } elseif ($node instanceof PHPParser_Node_Stmt_Trait) {
  46. $this->addNamespacedName($node);
  47. } elseif ($node instanceof PHPParser_Node_Stmt_Function) {
  48. $this->addNamespacedName($node);
  49. } elseif ($node instanceof PHPParser_Node_Stmt_Const) {
  50. foreach ($node->consts as $const) {
  51. $this->addNamespacedName($const);
  52. }
  53. } elseif ($node instanceof PHPParser_Node_Expr_StaticCall
  54. || $node instanceof PHPParser_Node_Expr_StaticPropertyFetch
  55. || $node instanceof PHPParser_Node_Expr_ClassConstFetch
  56. || $node instanceof PHPParser_Node_Expr_New
  57. || $node instanceof PHPParser_Node_Expr_Instanceof
  58. ) {
  59. if ($node->class instanceof PHPParser_Node_Name) {
  60. $node->class = $this->resolveClassName($node->class);
  61. }
  62. } elseif ($node instanceof PHPParser_Node_Stmt_Catch) {
  63. $node->type = $this->resolveClassName($node->type);
  64. } elseif ($node instanceof PHPParser_Node_Expr_FuncCall
  65. || $node instanceof PHPParser_Node_Expr_ConstFetch
  66. ) {
  67. if ($node->name instanceof PHPParser_Node_Name) {
  68. $node->name = $this->resolveOtherName($node->name);
  69. }
  70. } elseif ($node instanceof PHPParser_Node_Stmt_TraitUse) {
  71. foreach ($node->traits as &$trait) {
  72. $trait = $this->resolveClassName($trait);
  73. }
  74. } elseif ($node instanceof PHPParser_Node_Param
  75. && $node->type instanceof PHPParser_Node_Name
  76. ) {
  77. $node->type = $this->resolveClassName($node->type);
  78. }
  79. }
  80. protected function resolveClassName(PHPParser_Node_Name $name) {
  81. // don't resolve special class names
  82. if (in_array((string) $name, array('self', 'parent', 'static'))) {
  83. return $name;
  84. }
  85. // fully qualified names are already resolved
  86. if ($name->isFullyQualified()) {
  87. return $name;
  88. }
  89. // resolve aliases (for non-relative names)
  90. $aliasName = strtolower($name->getFirst());
  91. if (!$name->isRelative() && isset($this->aliases[$aliasName])) {
  92. $name->setFirst($this->aliases[$aliasName]);
  93. // if no alias exists prepend current namespace
  94. } elseif (null !== $this->namespace) {
  95. $name->prepend($this->namespace);
  96. }
  97. return new PHPParser_Node_Name_FullyQualified($name->parts, $name->getAttributes());
  98. }
  99. protected function resolveOtherName(PHPParser_Node_Name $name) {
  100. // fully qualified names are already resolved and we can't do anything about unqualified
  101. // ones at compiler-time
  102. if ($name->isFullyQualified() || $name->isUnqualified()) {
  103. return $name;
  104. }
  105. // resolve aliases for qualified names
  106. $aliasName = strtolower($name->getFirst());
  107. if ($name->isQualified() && isset($this->aliases[$aliasName])) {
  108. $name->setFirst($this->aliases[$aliasName]);
  109. // prepend namespace for relative names
  110. } elseif (null !== $this->namespace) {
  111. $name->prepend($this->namespace);
  112. }
  113. return new PHPParser_Node_Name_FullyQualified($name->parts, $name->getAttributes());
  114. }
  115. protected function addNamespacedName(PHPParser_Node $node) {
  116. if (null !== $this->namespace) {
  117. $node->namespacedName = clone $this->namespace;
  118. $node->namespacedName->append($node->name);
  119. } else {
  120. $node->namespacedName = new PHPParser_Node_Name($node->name, $node->getAttributes());
  121. }
  122. }
  123. }