Name.php 8.5 KB

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