RouteManager::prepareControllerConverters()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
c 0
b 0
f 0
rs 8.8571
cc 2
eloc 16
nc 2
nop 2
1
<?php
2
3
namespace Saxulum\RouteController\Manager;
4
5
use PhpParser\Node\Arg;
6
use PhpParser\Node\Expr\ArrayDimFetch;
7
use PhpParser\Node\Expr\Assign;
8
use PhpParser\Node\Expr\Closure;
9
use PhpParser\Node\Expr\ClosureUse;
10
use PhpParser\Node\Expr\MethodCall;
11
use PhpParser\Node\Expr\Variable;
12
use PhpParser\Node\Expr;
13
use PhpParser\Node\Name;
14
use PhpParser\Node\Param;
15
use PhpParser\Node\Scalar\DNumber;
16
use PhpParser\Node\Scalar\LNumber;
17
use PhpParser\Node\Scalar\String_;
18
use PhpParser\Node\Stmt\Return_;
19
use PhpParser\NodeAbstract;
20
use Saxulum\RouteController\Annotation\Callback as CallbackAnnotation;
21
use Saxulum\RouteController\Annotation\Convert;
22
use Saxulum\RouteController\Annotation\Route;
23
use Saxulum\AnnotationManager\Helper\ClassInfo;
24
use Saxulum\AnnotationManager\Helper\MethodInfo;
25
26
class RouteManager
27
{
28
    /**
29
     * @param  ClassInfo $classInfo
30
     * @return Expr[]
31
     */
32
    public function generateCode(ClassInfo $classInfo)
33
    {
34
        $nodes = array();
35
        $nodes[] = $this->prepareControllersNode();
36
37
        foreach ($classInfo->getMethodInfos() as $methodInfo) {
38
            $route = $methodInfo->getFirstAnnotationInstanceof(
39
                'Saxulum\\RouteController\\Annotation\\Route'
40
            );
41
            if (!is_null($route)) {
42
                $nodes[] = $this->prepareControllerNode($classInfo, $methodInfo, $route);
43
                $nodes = array_merge($nodes, $this->prepareControllerSimple($route, 'bind'));
44
                $nodes = array_merge($nodes, $this->prepareControllerComplex($route, 'asserts', 'assert'));
45
                $nodes = array_merge($nodes, $this->prepareControllerComplex($route, 'values', 'value'));
46
                $nodes = array_merge($nodes, $this->prepareControllerConverters($classInfo, $route));
47
                $nodes = array_merge($nodes, $this->prepareControllerSimple($route, 'method'));
48
                $nodes = array_merge($nodes, $this->prepareControllerBoolean($route, 'requireHttp'));
49
                $nodes = array_merge($nodes, $this->prepareControllerBoolean($route, 'requireHttps'));
50
                $nodes = array_merge($nodes, $this->prepareControllerBefore($classInfo, $route));
51
                $nodes = array_merge($nodes, $this->prepareControllerAfter($classInfo, $route));
52
            }
53
        }
54
55
        $nodes[] = $this->prepareControllersMountNode($classInfo);
56
57
        return $nodes;
58
    }
59
60
    /**
61
     * @return Assign
62
     */
63
    protected function prepareControllersNode()
64
    {
65
        return new Assign(
66
            new Variable('controllers'),
67
            new ArrayDimFetch(
68
                new Variable('app'),
69
                new String_('controllers_factory')
70
            )
71
        );
72
    }
73
74
    /**
75
     * @param  ClassInfo  $classInfo
76
     * @param  MethodInfo $methodInfo
77
     * @param  Route      $route
78
     * @return Assign
79
     */
80
    protected function prepareControllerNode(ClassInfo $classInfo, MethodInfo $methodInfo, Route $route)
81
    {
82
        return new Assign(
83
            new Variable('controller'),
84
            new MethodCall(
85
                new Variable('controllers'),
86
                'match',
87
                array(
88
                    new Arg(
89
                        $this->prepareScalarArg($route->value)
90
                    ),
91
                    new Arg(
92
                        $this->prepareScalarArg(
93
                            $classInfo->getServiceId() . ':' . $methodInfo->getName()
94
                        )
95
                    )
96
                )
97
            )
98
        );
99
    }
100
101
    /**
102
     * @param  Route          $route
103
     * @param  string         $property
104
     * @return NodeAbstract[]
105
     */
106
    protected function prepareControllerSimple(Route $route, $property)
107
    {
108
        $nodes = array();
109
        if (!is_null($route->$property)) {
110
            $nodes[] = new MethodCall(
111
                new Variable('controller'),
112
                $property,
113
                array(
114
                    new Arg(
115
                        $this->prepareScalarArg($route->$property)
116
                    ),
117
                )
118
            );
119
        }
120
121
        return $nodes;
122
    }
123
124
    /**
125
     * @param  Route          $route
126
     * @param  string         $property
127
     * @param  string         $method
128
     * @return NodeAbstract[]
129
     */
130
    protected function prepareControllerComplex(Route $route, $property, $method)
131
    {
132
        $nodes = array();
133
        foreach ($route->$property as $key => $element) {
134
            $nodes[] = new MethodCall(
135
                new Variable('controller'),
136
                $method,
137
                array(
138
                    new Arg(
139
                        $this->prepareScalarArg($key)
140
                    ),
141
                    new Arg(
142
                        $this->prepareScalarArg($element)
143
                    )
144
                )
145
            );
146
        }
147
148
        return $nodes;
149
    }
150
151
    /**
152
     * @param  ClassInfo      $classInfo
153
     * @param  Route          $route
154
     * @return NodeAbstract[]
155
     */
156
    protected function prepareControllerConverters(ClassInfo $classInfo, Route $route)
157
    {
158
        $nodes = array();
159
        foreach ($route->converters as $converter) {
160
            /** @var Convert $converter */
161
162
            $nodes[] = new MethodCall(
163
                new Variable('controller'),
164
                'convert',
165
                array(
166
                    new Arg(
167
                        $this->prepareScalarArg($converter->value)
168
                    ),
169
                    new Arg(
170
                        $this->prepareCallbackNode(
171
                            $classInfo,
172
                            $converter->callback->value,
173
                            'prepareControllerConverterClosure',
174
                            $converter->value
175
                        )
176
                    )
177
                )
178
            );
179
        }
180
181
        return $nodes;
182
    }
183
184
    /**
185
     * @param  ClassInfo   $classInfo
186
     * @param  string      $callback
187
     * @param  string      $callbackMethod
188
     * @param  string|null $value
189
     * @return Expr
190
     */
191
    protected function prepareCallbackNode(ClassInfo $classInfo, $callback, $callbackMethod, $value = null)
192
    {
193
        // controller as service callback
194
        if (preg_match('/^([^:]+):([^:]+)$/', $callback, $matches) === 1) {
195
196
            if ($matches[1] == '__self') {
197
                $matches[1] = $classInfo->getServiceId();
198
            }
199
200
            return  $this->$callbackMethod(
201
                $matches[1],
202
                $matches[2],
203
                $value
204
            );
205
        } elseif (preg_match('/^([^:]+)::([^:]+)$/', $callback, $matches) === 1) {
206
207
            if ($matches[1] == '__self') {
208
                $matches[1] = $classInfo->getName();
209
            }
210
211
            return new String_($matches[1] . '::' . $matches[2]);
212
        } else {
213
            return new String_($callback);
214
        }
215
    }
216
217
    /**
218
     * @param  string  $serviceId
219
     * @param  string  $methodName
220
     * @param  string  $variable
221
     * @return Closure
222
     */
223
    protected function prepareControllerConverterClosure($serviceId, $methodName, $variable)
224
    {
225
        return new Closure(
226
            array(
227
                'params' => array(
228
                    new Variable($variable)
229
                ),
230
                'uses' => array(
231
                    new ClosureUse('app')
232
                ),
233
                'stmts' => array(
234
                    new Return_(
235
                        new MethodCall(
236
                            new ArrayDimFetch(
237
                                new Variable('app'),
238
                                new String_($serviceId)
239
                            ),
240
                            $methodName,
241
                            array(
242
                                new Arg(
243
                                    new Variable($variable)
244
                                )
245
                            )
246
                        )
247
                    )
248
                )
249
            )
250
        );
251
    }
252
253
    /**
254
     * @param  Route          $route
255
     * @param  string         $property
256
     * @return NodeAbstract[]
257
     */
258
    protected function prepareControllerBoolean(Route $route, $property)
259
    {
260
        $nodes = array();
261
        if ($route->$property) {
262
            $nodes[] = new MethodCall(
263
                new Variable('controller'),
264
                $property
265
            );
266
        }
267
268
        return $nodes;
269
    }
270
271
    /**
272
     * @param  ClassInfo      $classInfo
273
     * @param  Route          $route
274
     * @return NodeAbstract[]
275
     */
276
    protected function prepareControllerBefore(ClassInfo $classInfo, Route $route)
277
    {
278
        $nodes = array();
279
        foreach ($route->before as $before) {
280
            $nodes[] = $this->prepareControllerCallback(
281
                $classInfo,
282
                $before,
283
                'before',
284
                'prepareControllerBeforeClosure'
285
            );
286
        }
287
288
        return $nodes;
289
    }
290
291
    /**
292
     * @param  ClassInfo          $classInfo
293
     * @param  CallbackAnnotation $annotation
294
     * @param  string             $method
295
     * @param  string             $callbackMethod
296
     * @return MethodCall
297
     */
298
    protected function prepareControllerCallback(ClassInfo $classInfo, CallbackAnnotation $annotation, $method, $callbackMethod)
299
    {
300
        /** @var CallbackAnnotation $annotation */
301
302
        return new MethodCall(
303
            new Variable('controller'),
304
            $method,
305
            array(
306
                new Arg(
307
                    $this->prepareCallbackNode(
308
                        $classInfo,
309
                        $annotation->value,
310
                        $callbackMethod
311
                    )
312
                )
313
            )
314
        );
315
    }
316
317
    /**
318
     * @param  string  $serviceId
319
     * @param  string  $methodName
320
     * @return Closure
321
     */
322
    protected function prepareControllerBeforeClosure($serviceId, $methodName)
323
    {
324
        return new Closure(
325
            array(
326
                'params' => array(
327
                    new Param(
328
                        'request',
329
                        null,
330
                        new Name('Symfony\Component\HttpFoundation\Request')
331
                    )
332
                ),
333
                'uses' => array(
334
                    new ClosureUse('app')
335
                ),
336
                'stmts' => array(
337
                    new Return_(
338
                        new MethodCall(
339
                            new ArrayDimFetch(
340
                                new Variable('app'),
341
                                new String_($serviceId)
342
                            ),
343
                            $methodName,
344
                            array(
345
                                new Arg(
346
                                    new Variable('request')
347
                                )
348
                            )
349
                        )
350
                    )
351
                )
352
            )
353
        );
354
    }
355
356
    /**
357
     * @param  ClassInfo      $classInfo
358
     * @param  Route          $route
359
     * @return NodeAbstract[]
360
     */
361
    protected function prepareControllerAfter(ClassInfo $classInfo, Route $route)
362
    {
363
        $nodes = array();
364
        foreach ($route->after as $after) {
365
            $nodes[] = $this->prepareControllerCallback(
366
                $classInfo,
367
                $after,
368
                'after',
369
                'prepareControllerAfterClosure'
370
            );
371
        }
372
373
        return $nodes;
374
    }
375
376
    /**
377
     * @param  string  $serviceId
378
     * @param  string  $methodName
379
     * @return Closure
380
     */
381
    protected function prepareControllerAfterClosure($serviceId, $methodName)
382
    {
383
        return new Closure(
384
            array(
385
                'params' => array(
386
                    new Param(
387
                        'request',
388
                        null,
389
                        new Name('Symfony\Component\HttpFoundation\Request')
390
                    ),
391
                    new Param(
392
                        'response',
393
                        null,
394
                        new Name('Symfony\Component\HttpFoundation\Response')
395
                    )
396
                ),
397
                'uses' => array(
398
                    new ClosureUse('app')
399
                ),
400
                'stmts' => array(
401
                    new Return_(
402
                        new MethodCall(
403
                            new ArrayDimFetch(
404
                                new Variable('app'),
405
                                new String_($serviceId)
406
                            ),
407
                            $methodName,
408
                            array(
409
                                new Arg(
410
                                    new Variable('request')
411
                                ),
412
                                new Arg(
413
                                    new Variable('response')
414
                                )
415
                            )
416
                        )
417
                    )
418
                )
419
            )
420
        );
421
    }
422
423
    /**
424
     * @param  ClassInfo  $classInfo
425
     * @return MethodCall
426
     */
427
    protected function prepareControllersMountNode(ClassInfo $classInfo)
428
    {
429
        $mount = '';
430
431
        $route = $classInfo->getFirstAnnotationInstanceof(
432
            'Saxulum\\RouteController\\Annotation\\Route'
433
        );
434
435
        if (!is_null($route)) {
436
            $mount = $route->value;
437
        }
438
439
        return new MethodCall(
440
            new Variable('app'),
441
            'mount',
442
            array(
443
                new Arg(
444
                    $this->prepareScalarArg($mount)
445
                ),
446
                new Arg(
447
                    new Variable('controllers')
448
                )
449
            )
450
        );
451
    }
452
453
    /**
454
     * @param $value
455
     * @return Expr
456
     */
457
    protected function prepareScalarArg($value)
458
    {
459
        if ($value === null) {
460
            return new Expr\ConstFetch(new Name('null'));
461
        }
462
463
        if (is_float($value)) {
464
            return new DNumber($value);
465
        }
466
467
        if (is_int($value)) {
468
            return new LNumber($value);
469
        }
470
471
        return new String_($value);
472
    }
473
}
474