Name.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php declare(strict_types=1);
  2. namespace PhpParser\Node;
  3. use PhpParser\NodeAbstract;
  4. class Name extends NodeAbstract
  5. {
  6. /**
  7. * @var string[] Parts of the name
  8. * @deprecated Use getParts() instead
  9. */
  10. public $parts;
  11. private static $specialClassNames = [
  12. 'self' => true,
  13. 'parent' => true,
  14. 'static' => true,
  15. ];
  16. /**
  17. * Constructs a name node.
  18. *
  19. * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor)
  20. * @param array $attributes Additional attributes
  21. */
  22. public function __construct($name, array $attributes = []) {
  23. $this->attributes = $attributes;
  24. $this->parts = self::prepareName($name);
  25. }
  26. public function getSubNodeNames() : array {
  27. return ['parts'];
  28. }
  29. /**
  30. * Get parts of name (split by the namespace separator).
  31. *
  32. * @return string[] Parts of name
  33. */
  34. public function getParts(): array {
  35. return $this->parts;
  36. }
  37. /**
  38. * Gets the first part of the name, i.e. everything before the first namespace separator.
  39. *
  40. * @return string First part of the name
  41. */
  42. public function getFirst() : string {
  43. return $this->parts[0];
  44. }
  45. /**
  46. * Gets the last part of the name, i.e. everything after the last namespace separator.
  47. *
  48. * @return string Last part of the name
  49. */
  50. public function getLast() : string {
  51. return $this->parts[count($this->parts) - 1];
  52. }
  53. /**
  54. * Checks whether the name is unqualified. (E.g. Name)
  55. *
  56. * @return bool Whether the name is unqualified
  57. */
  58. public function isUnqualified() : bool {
  59. return 1 === count($this->parts);
  60. }
  61. /**
  62. * Checks whether the name is qualified. (E.g. Name\Name)
  63. *
  64. * @return bool Whether the name is qualified
  65. */
  66. public function isQualified() : bool {
  67. return 1 < count($this->parts);
  68. }
  69. /**
  70. * Checks whether the name is fully qualified. (E.g. \Name)
  71. *
  72. * @return bool Whether the name is fully qualified
  73. */
  74. public function isFullyQualified() : bool {
  75. return false;
  76. }
  77. /**
  78. * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
  79. *
  80. * @return bool Whether the name is relative
  81. */
  82. public function isRelative() : bool {
  83. return false;
  84. }
  85. /**
  86. * Returns a string representation of the name itself, without taking the name type into
  87. * account (e.g., not including a leading backslash for fully qualified names).
  88. *
  89. * @return string String representation
  90. */
  91. public function toString() : string {
  92. return implode('\\', $this->parts);
  93. }
  94. /**
  95. * Returns a string representation of the name as it would occur in code (e.g., including
  96. * leading backslash for fully qualified names.
  97. *
  98. * @return string String representation
  99. */
  100. public function toCodeString() : string {
  101. return $this->toString();
  102. }
  103. /**
  104. * Returns lowercased string representation of the name, without taking the name type into
  105. * account (e.g., no leading backslash for fully qualified names).
  106. *
  107. * @return string Lowercased string representation
  108. */
  109. public function toLowerString() : string {
  110. return strtolower(implode('\\', $this->parts));
  111. }
  112. /**
  113. * Checks whether the identifier is a special class name (self, parent or static).
  114. *
  115. * @return bool Whether identifier is a special class name
  116. */
  117. public function isSpecialClassName() : bool {
  118. return count($this->parts) === 1
  119. && isset(self::$specialClassNames[strtolower($this->parts[0])]);
  120. }
  121. /**
  122. * Returns a string representation of the name by imploding the namespace parts with the
  123. * namespace separator.
  124. *
  125. * @return string String representation
  126. */
  127. public function __toString() : string {
  128. return implode('\\', $this->parts);
  129. }
  130. /**
  131. * Gets a slice of a name (similar to array_slice).
  132. *
  133. * This method returns a new instance of the same type as the original and with the same
  134. * attributes.
  135. *
  136. * If the slice is empty, null is returned. The null value will be correctly handled in
  137. * concatenations using concat().
  138. *
  139. * Offset and length have the same meaning as in array_slice().
  140. *
  141. * @param int $offset Offset to start the slice at (may be negative)
  142. * @param int|null $length Length of the slice (may be negative)
  143. *
  144. * @return static|null Sliced name
  145. */
  146. public function slice(int $offset, int $length = null) {
  147. $numParts = count($this->parts);
  148. $realOffset = $offset < 0 ? $offset + $numParts : $offset;
  149. if ($realOffset < 0 || $realOffset > $numParts) {
  150. throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
  151. }
  152. if (null === $length) {
  153. $realLength = $numParts - $realOffset;
  154. } else {
  155. $realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
  156. if ($realLength < 0 || $realLength > $numParts - $realOffset) {
  157. throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
  158. }
  159. }
  160. if ($realLength === 0) {
  161. // Empty slice is represented as null
  162. return null;
  163. }
  164. return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes);
  165. }
  166. /**
  167. * Concatenate two names, yielding a new Name instance.
  168. *
  169. * The type of the generated instance depends on which class this method is called on, for
  170. * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance.
  171. *
  172. * If one of the arguments is null, a new instance of the other name will be returned. If both
  173. * arguments are null, null will be returned. As such, writing
  174. * Name::concat($namespace, $shortName)
  175. * where $namespace is a Name node or null will work as expected.
  176. *
  177. * @param string|string[]|self|null $name1 The first name
  178. * @param string|string[]|self|null $name2 The second name
  179. * @param array $attributes Attributes to assign to concatenated name
  180. *
  181. * @return static|null Concatenated name
  182. */
  183. public static function concat($name1, $name2, array $attributes = []) {
  184. if (null === $name1 && null === $name2) {
  185. return null;
  186. } elseif (null === $name1) {
  187. return new static(self::prepareName($name2), $attributes);
  188. } elseif (null === $name2) {
  189. return new static(self::prepareName($name1), $attributes);
  190. } else {
  191. return new static(
  192. array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes
  193. );
  194. }
  195. }
  196. /**
  197. * Prepares a (string, array or Name node) name for use in name changing methods by converting
  198. * it to an array.
  199. *
  200. * @param string|string[]|self $name Name to prepare
  201. *
  202. * @return string[] Prepared name
  203. */
  204. private static function prepareName($name) : array {
  205. if (\is_string($name)) {
  206. if ('' === $name) {
  207. throw new \InvalidArgumentException('Name cannot be empty');
  208. }
  209. return explode('\\', $name);
  210. } elseif (\is_array($name)) {
  211. if (empty($name)) {
  212. throw new \InvalidArgumentException('Name cannot be empty');
  213. }
  214. return $name;
  215. } elseif ($name instanceof self) {
  216. return $name->parts;
  217. }
  218. throw new \InvalidArgumentException(
  219. 'Expected string, array of parts or Name instance'
  220. );
  221. }
  222. public function getType() : string {
  223. return 'Name';
  224. }
  225. }