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