BuilderHelpers.php 9.8 KB

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