BuilderHelpers.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php declare(strict_types=1);
  2. namespace PhpParser;
  3. use PhpParser\Node\Expr;
  4. use PhpParser\Node\Identifier;
  5. use PhpParser\Node\Name;
  6. use PhpParser\Node\NullableType;
  7. use PhpParser\Node\Scalar;
  8. use PhpParser\Node\Stmt;
  9. use PhpParser\Node\UnionType;
  10. /**
  11. * This class defines helpers used in the implementation of builders. Don't use it directly.
  12. *
  13. * @internal
  14. */
  15. final class BuilderHelpers
  16. {
  17. /**
  18. * Normalizes a node: Converts builder objects to nodes.
  19. *
  20. * @param Node|Builder $node The node to normalize
  21. *
  22. * @return Node The normalized node
  23. */
  24. public static function normalizeNode($node) : Node {
  25. if ($node instanceof Builder) {
  26. return $node->getNode();
  27. } elseif ($node instanceof Node) {
  28. return $node;
  29. }
  30. throw new \LogicException('Expected node or builder object');
  31. }
  32. /**
  33. * Normalizes a node to a statement.
  34. *
  35. * Expressions are wrapped in a Stmt\Expression node.
  36. *
  37. * @param Node|Builder $node The node to normalize
  38. *
  39. * @return Stmt The normalized statement node
  40. */
  41. public static function normalizeStmt($node) : Stmt {
  42. $node = self::normalizeNode($node);
  43. if ($node instanceof Stmt) {
  44. return $node;
  45. }
  46. if ($node instanceof Expr) {
  47. return new Stmt\Expression($node);
  48. }
  49. throw new \LogicException('Expected statement or expression node');
  50. }
  51. /**
  52. * Normalizes strings to Identifier.
  53. *
  54. * @param string|Identifier $name The identifier to normalize
  55. *
  56. * @return Identifier The normalized identifier
  57. */
  58. public static function normalizeIdentifier($name) : Identifier {
  59. if ($name instanceof Identifier) {
  60. return $name;
  61. }
  62. if (\is_string($name)) {
  63. return new Identifier($name);
  64. }
  65. throw new \LogicException('Expected string or instance of Node\Identifier');
  66. }
  67. /**
  68. * Normalizes strings to Identifier, also allowing expressions.
  69. *
  70. * @param string|Identifier|Expr $name The identifier to normalize
  71. *
  72. * @return Identifier|Expr The normalized identifier or expression
  73. */
  74. public static function normalizeIdentifierOrExpr($name) {
  75. if ($name instanceof Identifier || $name instanceof Expr) {
  76. return $name;
  77. }
  78. if (\is_string($name)) {
  79. return new Identifier($name);
  80. }
  81. throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr');
  82. }
  83. /**
  84. * Normalizes a name: Converts string names to Name nodes.
  85. *
  86. * @param Name|string $name The name to normalize
  87. *
  88. * @return Name The normalized name
  89. */
  90. public static function normalizeName($name) : Name {
  91. return self::normalizeNameCommon($name, false);
  92. }
  93. /**
  94. * Normalizes a name: Converts string names to Name nodes, while also allowing expressions.
  95. *
  96. * @param Expr|Name|string $name The name to normalize
  97. *
  98. * @return Name|Expr The normalized name or expression
  99. */
  100. public static function normalizeNameOrExpr($name) {
  101. return self::normalizeNameCommon($name, true);
  102. }
  103. /**
  104. * Normalizes a name: Converts string names to Name nodes, optionally allowing expressions.
  105. *
  106. * @param Expr|Name|string $name The name to normalize
  107. * @param bool $allowExpr Whether to also allow expressions
  108. *
  109. * @return Name|Expr The normalized name, or expression (if allowed)
  110. */
  111. private static function normalizeNameCommon($name, bool $allowExpr) {
  112. if ($name instanceof Name) {
  113. return $name;
  114. } elseif (is_string($name)) {
  115. if (!$name) {
  116. throw new \LogicException('Name cannot be empty');
  117. }
  118. if ($name[0] === '\\') {
  119. return new Name\FullyQualified(substr($name, 1));
  120. } elseif (0 === strpos($name, 'namespace\\')) {
  121. return new Name\Relative(substr($name, strlen('namespace\\')));
  122. } else {
  123. return new Name($name);
  124. }
  125. }
  126. if ($allowExpr) {
  127. if ($name instanceof Expr) {
  128. return $name;
  129. }
  130. throw new \LogicException(
  131. 'Name must be a string or an instance of Node\Name or Node\Expr'
  132. );
  133. } else {
  134. throw new \LogicException('Name must be a string or an instance of Node\Name');
  135. }
  136. }
  137. /**
  138. * Normalizes a type: Converts plain-text type names into proper AST representation.
  139. *
  140. * In particular, builtin types become Identifiers, custom types become Names and nullables
  141. * are wrapped in NullableType nodes.
  142. *
  143. * @param string|Name|Identifier|NullableType|UnionType $type The type to normalize
  144. *
  145. * @return Name|Identifier|NullableType|UnionType The normalized type
  146. */
  147. public static function normalizeType($type) {
  148. if (!is_string($type)) {
  149. if (
  150. !$type instanceof Name && !$type instanceof Identifier &&
  151. !$type instanceof NullableType && !$type instanceof UnionType
  152. ) {
  153. throw new \LogicException(
  154. 'Type must be a string, or an instance of Name, Identifier, NullableType or UnionType'
  155. );
  156. }
  157. return $type;
  158. }
  159. $nullable = false;
  160. if (strlen($type) > 0 && $type[0] === '?') {
  161. $nullable = true;
  162. $type = substr($type, 1);
  163. }
  164. $builtinTypes = [
  165. 'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object', 'mixed'
  166. ];
  167. $lowerType = strtolower($type);
  168. if (in_array($lowerType, $builtinTypes)) {
  169. $type = new Identifier($lowerType);
  170. } else {
  171. $type = self::normalizeName($type);
  172. }
  173. if ($nullable && (string) $type === 'void') {
  174. throw new \LogicException('void type cannot be nullable');
  175. }
  176. if ($nullable && (string) $type === 'mixed') {
  177. throw new \LogicException('mixed type cannot be nullable');
  178. }
  179. return $nullable ? new NullableType($type) : $type;
  180. }
  181. /**
  182. * Normalizes a value: Converts nulls, booleans, integers,
  183. * floats, strings and arrays into their respective nodes
  184. *
  185. * @param Node\Expr|bool|null|int|float|string|array $value The value to normalize
  186. *
  187. * @return Expr The normalized value
  188. */
  189. public static function normalizeValue($value) : Expr {
  190. if ($value instanceof Node\Expr) {
  191. return $value;
  192. } elseif (is_null($value)) {
  193. return new Expr\ConstFetch(
  194. new Name('null')
  195. );
  196. } elseif (is_bool($value)) {
  197. return new Expr\ConstFetch(
  198. new Name($value ? 'true' : 'false')
  199. );
  200. } elseif (is_int($value)) {
  201. return new Scalar\LNumber($value);
  202. } elseif (is_float($value)) {
  203. return new Scalar\DNumber($value);
  204. } elseif (is_string($value)) {
  205. return new Scalar\String_($value);
  206. } elseif (is_array($value)) {
  207. $items = [];
  208. $lastKey = -1;
  209. foreach ($value as $itemKey => $itemValue) {
  210. // for consecutive, numeric keys don't generate keys
  211. if (null !== $lastKey && ++$lastKey === $itemKey) {
  212. $items[] = new Expr\ArrayItem(
  213. self::normalizeValue($itemValue)
  214. );
  215. } else {
  216. $lastKey = null;
  217. $items[] = new Expr\ArrayItem(
  218. self::normalizeValue($itemValue),
  219. self::normalizeValue($itemKey)
  220. );
  221. }
  222. }
  223. return new Expr\Array_($items);
  224. } else {
  225. throw new \LogicException('Invalid value');
  226. }
  227. }
  228. /**
  229. * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc.
  230. *
  231. * @param Comment\Doc|string $docComment The doc comment to normalize
  232. *
  233. * @return Comment\Doc The normalized doc comment
  234. */
  235. public static function normalizeDocComment($docComment) : Comment\Doc {
  236. if ($docComment instanceof Comment\Doc) {
  237. return $docComment;
  238. } elseif (is_string($docComment)) {
  239. return new Comment\Doc($docComment);
  240. } else {
  241. throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
  242. }
  243. }
  244. /**
  245. * Adds a modifier and returns new modifier bitmask.
  246. *
  247. * @param int $modifiers Existing modifiers
  248. * @param int $modifier Modifier to set
  249. *
  250. * @return int New modifiers
  251. */
  252. public static function addModifier(int $modifiers, int $modifier) : int {
  253. Stmt\Class_::verifyModifier($modifiers, $modifier);
  254. return $modifiers | $modifier;
  255. }
  256. }