1<?php declare(strict_types=1);
2
3namespace PhpParser;
4
5use PhpParser\Node\Arg;
6use PhpParser\Node\Expr;
7use PhpParser\Node\Expr\BinaryOp\Concat;
8use PhpParser\Node\Identifier;
9use PhpParser\Node\Name;
10use PhpParser\Node\Scalar\String_;
11use PhpParser\Node\Stmt\Use_;
12
13class BuilderFactory {
14    /**
15     * Creates an attribute node.
16     *
17     * @param string|Name $name Name of the attribute
18     * @param array $args Attribute named arguments
19     */
20    public function attribute($name, array $args = []): Node\Attribute {
21        return new Node\Attribute(
22            BuilderHelpers::normalizeName($name),
23            $this->args($args)
24        );
25    }
26
27    /**
28     * Creates a namespace builder.
29     *
30     * @param null|string|Node\Name $name Name of the namespace
31     *
32     * @return Builder\Namespace_ The created namespace builder
33     */
34    public function namespace($name): Builder\Namespace_ {
35        return new Builder\Namespace_($name);
36    }
37
38    /**
39     * Creates a class builder.
40     *
41     * @param string $name Name of the class
42     *
43     * @return Builder\Class_ The created class builder
44     */
45    public function class(string $name): Builder\Class_ {
46        return new Builder\Class_($name);
47    }
48
49    /**
50     * Creates an interface builder.
51     *
52     * @param string $name Name of the interface
53     *
54     * @return Builder\Interface_ The created interface builder
55     */
56    public function interface(string $name): Builder\Interface_ {
57        return new Builder\Interface_($name);
58    }
59
60    /**
61     * Creates a trait builder.
62     *
63     * @param string $name Name of the trait
64     *
65     * @return Builder\Trait_ The created trait builder
66     */
67    public function trait(string $name): Builder\Trait_ {
68        return new Builder\Trait_($name);
69    }
70
71    /**
72     * Creates an enum builder.
73     *
74     * @param string $name Name of the enum
75     *
76     * @return Builder\Enum_ The created enum builder
77     */
78    public function enum(string $name): Builder\Enum_ {
79        return new Builder\Enum_($name);
80    }
81
82    /**
83     * Creates a trait use builder.
84     *
85     * @param Node\Name|string ...$traits Trait names
86     *
87     * @return Builder\TraitUse The created trait use builder
88     */
89    public function useTrait(...$traits): Builder\TraitUse {
90        return new Builder\TraitUse(...$traits);
91    }
92
93    /**
94     * Creates a trait use adaptation builder.
95     *
96     * @param Node\Name|string|null $trait Trait name
97     * @param Node\Identifier|string $method Method name
98     *
99     * @return Builder\TraitUseAdaptation The created trait use adaptation builder
100     */
101    public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation {
102        if ($method === null) {
103            $method = $trait;
104            $trait = null;
105        }
106
107        return new Builder\TraitUseAdaptation($trait, $method);
108    }
109
110    /**
111     * Creates a method builder.
112     *
113     * @param string $name Name of the method
114     *
115     * @return Builder\Method The created method builder
116     */
117    public function method(string $name): Builder\Method {
118        return new Builder\Method($name);
119    }
120
121    /**
122     * Creates a parameter builder.
123     *
124     * @param string $name Name of the parameter
125     *
126     * @return Builder\Param The created parameter builder
127     */
128    public function param(string $name): Builder\Param {
129        return new Builder\Param($name);
130    }
131
132    /**
133     * Creates a property builder.
134     *
135     * @param string $name Name of the property
136     *
137     * @return Builder\Property The created property builder
138     */
139    public function property(string $name): Builder\Property {
140        return new Builder\Property($name);
141    }
142
143    /**
144     * Creates a function builder.
145     *
146     * @param string $name Name of the function
147     *
148     * @return Builder\Function_ The created function builder
149     */
150    public function function(string $name): Builder\Function_ {
151        return new Builder\Function_($name);
152    }
153
154    /**
155     * Creates a namespace/class use builder.
156     *
157     * @param Node\Name|string $name Name of the entity (namespace or class) to alias
158     *
159     * @return Builder\Use_ The created use builder
160     */
161    public function use($name): Builder\Use_ {
162        return new Builder\Use_($name, Use_::TYPE_NORMAL);
163    }
164
165    /**
166     * Creates a function use builder.
167     *
168     * @param Node\Name|string $name Name of the function to alias
169     *
170     * @return Builder\Use_ The created use function builder
171     */
172    public function useFunction($name): Builder\Use_ {
173        return new Builder\Use_($name, Use_::TYPE_FUNCTION);
174    }
175
176    /**
177     * Creates a constant use builder.
178     *
179     * @param Node\Name|string $name Name of the const to alias
180     *
181     * @return Builder\Use_ The created use const builder
182     */
183    public function useConst($name): Builder\Use_ {
184        return new Builder\Use_($name, Use_::TYPE_CONSTANT);
185    }
186
187    /**
188     * Creates a class constant builder.
189     *
190     * @param string|Identifier $name Name
191     * @param Node\Expr|bool|null|int|float|string|array $value Value
192     *
193     * @return Builder\ClassConst The created use const builder
194     */
195    public function classConst($name, $value): Builder\ClassConst {
196        return new Builder\ClassConst($name, $value);
197    }
198
199    /**
200     * Creates an enum case builder.
201     *
202     * @param string|Identifier $name Name
203     *
204     * @return Builder\EnumCase The created use const builder
205     */
206    public function enumCase($name): Builder\EnumCase {
207        return new Builder\EnumCase($name);
208    }
209
210    /**
211     * Creates node a for a literal value.
212     *
213     * @param Expr|bool|null|int|float|string|array|\UnitEnum $value $value
214     */
215    public function val($value): Expr {
216        return BuilderHelpers::normalizeValue($value);
217    }
218
219    /**
220     * Creates variable node.
221     *
222     * @param string|Expr $name Name
223     */
224    public function var($name): Expr\Variable {
225        if (!\is_string($name) && !$name instanceof Expr) {
226            throw new \LogicException('Variable name must be string or Expr');
227        }
228
229        return new Expr\Variable($name);
230    }
231
232    /**
233     * Normalizes an argument list.
234     *
235     * Creates Arg nodes for all arguments and converts literal values to expressions.
236     *
237     * @param array $args List of arguments to normalize
238     *
239     * @return list<Arg>
240     */
241    public function args(array $args): array {
242        $normalizedArgs = [];
243        foreach ($args as $key => $arg) {
244            if (!($arg instanceof Arg)) {
245                $arg = new Arg(BuilderHelpers::normalizeValue($arg));
246            }
247            if (\is_string($key)) {
248                $arg->name = BuilderHelpers::normalizeIdentifier($key);
249            }
250            $normalizedArgs[] = $arg;
251        }
252        return $normalizedArgs;
253    }
254
255    /**
256     * Creates a function call node.
257     *
258     * @param string|Name|Expr $name Function name
259     * @param array $args Function arguments
260     */
261    public function funcCall($name, array $args = []): Expr\FuncCall {
262        return new Expr\FuncCall(
263            BuilderHelpers::normalizeNameOrExpr($name),
264            $this->args($args)
265        );
266    }
267
268    /**
269     * Creates a method call node.
270     *
271     * @param Expr $var Variable the method is called on
272     * @param string|Identifier|Expr $name Method name
273     * @param array $args Method arguments
274     */
275    public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall {
276        return new Expr\MethodCall(
277            $var,
278            BuilderHelpers::normalizeIdentifierOrExpr($name),
279            $this->args($args)
280        );
281    }
282
283    /**
284     * Creates a static method call node.
285     *
286     * @param string|Name|Expr $class Class name
287     * @param string|Identifier|Expr $name Method name
288     * @param array $args Method arguments
289     */
290    public function staticCall($class, $name, array $args = []): Expr\StaticCall {
291        return new Expr\StaticCall(
292            BuilderHelpers::normalizeNameOrExpr($class),
293            BuilderHelpers::normalizeIdentifierOrExpr($name),
294            $this->args($args)
295        );
296    }
297
298    /**
299     * Creates an object creation node.
300     *
301     * @param string|Name|Expr $class Class name
302     * @param array $args Constructor arguments
303     */
304    public function new($class, array $args = []): Expr\New_ {
305        return new Expr\New_(
306            BuilderHelpers::normalizeNameOrExpr($class),
307            $this->args($args)
308        );
309    }
310
311    /**
312     * Creates a constant fetch node.
313     *
314     * @param string|Name $name Constant name
315     */
316    public function constFetch($name): Expr\ConstFetch {
317        return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
318    }
319
320    /**
321     * Creates a property fetch node.
322     *
323     * @param Expr $var Variable holding object
324     * @param string|Identifier|Expr $name Property name
325     */
326    public function propertyFetch(Expr $var, $name): Expr\PropertyFetch {
327        return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
328    }
329
330    /**
331     * Creates a class constant fetch node.
332     *
333     * @param string|Name|Expr $class Class name
334     * @param string|Identifier|Expr $name Constant name
335     */
336    public function classConstFetch($class, $name): Expr\ClassConstFetch {
337        return new Expr\ClassConstFetch(
338            BuilderHelpers::normalizeNameOrExpr($class),
339            BuilderHelpers::normalizeIdentifierOrExpr($name)
340        );
341    }
342
343    /**
344     * Creates nested Concat nodes from a list of expressions.
345     *
346     * @param Expr|string ...$exprs Expressions or literal strings
347     */
348    public function concat(...$exprs): Concat {
349        $numExprs = count($exprs);
350        if ($numExprs < 2) {
351            throw new \LogicException('Expected at least two expressions');
352        }
353
354        $lastConcat = $this->normalizeStringExpr($exprs[0]);
355        for ($i = 1; $i < $numExprs; $i++) {
356            $lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i]));
357        }
358        return $lastConcat;
359    }
360
361    /**
362     * @param string|Expr $expr
363     */
364    private function normalizeStringExpr($expr): Expr {
365        if ($expr instanceof Expr) {
366            return $expr;
367        }
368
369        if (\is_string($expr)) {
370            return new String_($expr);
371        }
372
373        throw new \LogicException('Expected string or Expr');
374    }
375}
376