BuilderHelpers.php 8.7 KB

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