Passed
Push — master ( a27896...7378e6 )
by Divine Niiquaye
02:56
created

Listener::getAnnotations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Flight\Routing\Annotation;
19
20
use Biurad\Annotations\{InvalidAnnotationException, ListenerInterface};
21
use Flight\Routing\{Route as BaseRoute, RouteCollection};
22
23
/**
24
 * The Biurad Annotation's Listener bridge.
25
 *
26
 * @author Divine Niiquaye Ibok <[email protected]>
27
 */
28
class Listener implements ListenerInterface
29
{
30
    private RouteCollection $collector;
31
    private ?string $unNamedPrefix;
32
33
    /** @var array<string,int> */
34
    private array $defaultUnnamedIndex = [];
35
36
    /**
37
     * @param string $unNamedPrefix Setting a prefix or empty string will generate a name for all routes.
38
     *                              If set to null, only named grouped class routes names will be generated.
39
     */
40 9
    public function __construct(RouteCollection $collector = null, ?string $unNamedPrefix = '')
41
    {
42 9
        $this->unNamedPrefix = $unNamedPrefix;
43 9
        $this->collector = $collector ?? new RouteCollection();
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 9
    public function load(array $annotations): RouteCollection
50
    {
51 9
        $foundAnnotations = [];
52
53 9
        foreach ($annotations as $annotation) {
54 9
            $reflection = $annotation['type'];
55 9
            $methodAnnotations = [];
56
57 9
            if (empty($methods = $annotation['methods'] ?? [])) {
58 6
                $this->getRoutes($annotation['attributes'], $reflection->name, $foundAnnotations, $reflection instanceof \ReflectionClass);
59 5
                continue;
60
            }
61
62 8
            foreach ($methods as $method) {
63 8
                $controller = ($m = $method['type'])->isStatic() ? $reflection->name . '::' . $m->name : [$reflection->name, $m->name];
64 8
                $this->getRoutes($method['attributes'], $controller, $methodAnnotations);
65
            }
66
67 7
            foreach ($methodAnnotations as $methodAnnotation) {
68 7
                if (empty($annotation['attributes'])) {
69 5
                    if (!empty($routeName = $this->resolveRouteName(null, $methodAnnotation))) {
70 5
                        $methodAnnotation->bind($routeName);
71
                    }
72
73 5
                    $foundAnnotations[] = $methodAnnotation;
74 5
                    continue;
75
                }
76
77 7
                foreach ($annotation['attributes'] as $classAnnotation) {
78 7
                    if (null !== $classAnnotation->resource) {
79 1
                        throw new InvalidAnnotationException('Restful annotated class cannot contain annotated method(s).');
80
                    }
81
82 6
                    $annotatedMethod = clone $methodAnnotation->method(...$classAnnotation->methods)
83 6
                            ->scheme(...$classAnnotation->schemes)
84 6
                            ->domain(...$classAnnotation->hosts)
85 6
                            ->defaults($classAnnotation->defaults)
86 6
                            ->arguments($classAnnotation->arguments)
87 6
                            ->asserts($classAnnotation->patterns);
88
89 6
                    if (null !== $classAnnotation->path) {
90 6
                        $annotatedMethod->prefix($classAnnotation->path);
91
                    }
92
93 6
                    if (!empty($routeName = $this->resolveRouteName($classAnnotation->name, $annotatedMethod, true))) {
94 6
                        $annotatedMethod->bind($routeName);
95
                    }
96
97 6
                    $foundAnnotations[] = $annotatedMethod;
98
                }
99
            }
100
        }
101
102 6
        return $this->collector->routes($foundAnnotations);
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 8
    public function getAnnotations(): array
109
    {
110 8
        return ['Flight\Routing\Annotation\Route'];
111
    }
112
113
    /**
114
     * @param array<int,Route> $annotations
115
     * @param mixed            $handler
116
     * @param BaseRoute[]      $foundAnnotations
117
     */
118 9
    protected function getRoutes(array $annotations, $handler, array &$foundAnnotations, bool $single = false): void
119
    {
120 9
        foreach ($annotations as $annotation) {
121 9
            if (!$single && null !== $annotation->resource) {
122 1
                throw new InvalidAnnotationException('Restful annotation is only supported on classes.');
123
            }
124
125 8
            $route = $annotation->getRoute($handler);
126
127 7
            if ($single && $routeName = $this->resolveRouteName(null, $route)) {
128 5
                $route->bind($routeName);
129
            }
130
131 7
            $foundAnnotations[] = $route;
132
        }
133
    }
134
135
    /**
136
     * Resolve route naming.
137
     */
138 6
    private function resolveRouteName(?string $prefix, BaseRoute $route, bool $force = false): string
139
    {
140 6
        $name = $route->getName();
141
142 6
        if ((null !== $this->unNamedPrefix || $force) && empty($name)) {
143 6
            $name = $base = $prefix . $route->generateRouteName($this->unNamedPrefix ?? '');
144
145 6
            if (isset($this->defaultUnnamedIndex[$name])) {
146 4
                $name = $base . '_' . ++$this->defaultUnnamedIndex[$name];
147
            } else {
148 6
                $this->defaultUnnamedIndex[$name] = 0;
149
            }
150
151 6
            return $name;
152
        }
153
154 6
        return (string) $prefix . $name;
155
    }
156
}
157