Test Failed
Pull Request — master (#13)
by Divine Niiquaye
02:21
created

Listener::getAnnotation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.1 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;
21
use Biurad\Annotations\ListenerInterface;
22
use Flight\Routing\Route as BaseRoute;
23
use Flight\Routing\RouteCollection;
24
use Flight\Routing\Router;
25
26
class Listener implements ListenerInterface
27
{
28
    /** @var RouteCollection */
29
    private $collector;
30
31
    /** @var int */
32
    private $defaultRouteIndex = 0;
33
34
    /**
35
     * @param null|RouteCollection $collector
36
     */
37
    public function __construct(?RouteCollection $collector = null)
38
    {
39 14
        $this->collector = $collector ?? new RouteCollection();
40
    }
41 14
42 14
    /**
43
     * {@inheritdoc}
44
     *
45
     * @param array<string,array<string,mixed>> $annotations
46
     */
47
    public function onAnnotation(array $annotations): RouteCollection
48
    {
49 4
        /** @var class-string $class */
50
        foreach ($annotations as $class => $collection) {
51
            if (isset($collection['method'])) {
52 4
                $this->addRouteGroup($collection['class'] ?? [], $collection['method']);
53 4
54 4
                continue;
55
            }
56 4
57
            $this->defaultRouteIndex = 0;
58
59 3
            /** @var Route $annotation */
60
            foreach ($collection['class'] ?? [] as $annotation) {
61 3
                $this->collector->add($this->addRoute($annotation, $class));
62 3
            }
63
        }
64
65
        return $this->collector;
66 4
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function getArguments(): array
72 3
    {
73
        return [];
74 3
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function getAnnotation(): string
80 4
    {
81
        return 'Flight\Routing\Annotation\Route';
82 4
    }
83
84
    /**
85
     * Add a route from annotation
86
     *
87
     * @param Route                 $annotation
88
     * @param class-string|string[] $handler
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|string[].
Loading history...
89
     * @param null|Route            $group
90
     *
91
     * @return BaseRoute
92
     */
93
    protected function addRoute(Route $annotation, $handler, ?Route $group = null): BaseRoute
94 4
    {
95
        if (null === $annotation->getPath()) {
96 4
            throw new InvalidAnnotationException('@Route.path must not be left empty.');
97
        }
98
99
        $name = $annotation->getName() ?? $this->getDefaultRouteName($handler);
100 4
101 4
        $methods = str_ends_with($name, '__restful') ? Router::HTTP_METHODS_STANDARD : $annotation->getMethods();
102 4
        $route   = new BaseRoute($annotation->getPath(), \join('|', $methods), $handler);
103
104 4
        $route->scheme(...$annotation->getSchemes())->middleware(...$annotation->getMiddlewares())->bind($name);
105 4
106 4
        if (null !== $annotation->getDomain()) {
107 4
            $route->domain($annotation->getDomain());
108 4
        }
109
110 4
        foreach ($annotation->getDefaults() as $variable => $default) {
111 4
            $route->default($variable, $default);
112
        }
113
114 4
        foreach ($annotation->getPatterns() as $variable => $regexp) {
115
            $route->assert($variable, $regexp);
116
        }
117
118
        if (null !== $group) {
119
            $route = $this->mergeGroup($group, $route);
120
        }
121
122
        return $route;
123 4
    }
124
125 4
    /**
126 4
     * Add a routes from annotation into group
127 4
     *
128
     * @param Route[] $grouping
129
     * @param mixed[] $methods
130 4
     */
131
    protected function addRouteGroup(array $grouping, array $methods): void
132
    {
133 3
        if (!empty($grouping)) {
134 3
            foreach ($grouping as $group) {
135
                $this->mergeAnnotations($methods, $group);
136
            }
137
138
            return;
139
        }
140 4
141
        $this->mergeAnnotations($methods);
142 4
    }
143
144 4
    /**
145
     * @param mixed[]    $methods
146 4
     * @param null|Route $group
147 4
     */
148
    protected function mergeAnnotations(array $methods, ?Route $group = null): void
149
    {
150 4
        $this->defaultRouteIndex = 0;
151 4
152
        $routes = [];
153
154
        foreach ($methods as [$method, $annotation]) {
155
            $routes[] = $this->addRoute($annotation, [$method->class, $method->getName()], $group);
156
        }
157
158
        $this->collector->add(...$routes);
159 4
    }
160
161 4
    /**
162 4
     * @param Route     $group
163 4
     * @param BaseRoute $route
164 4
     *
165 4
     * @return BaseRoute
166 4
     */
167 4
    protected function mergeGroup(Route $group, BaseRoute $route): BaseRoute
168 4
    {
169
        $route = $route->bind($group->getName() . $route->getName())
170 4
            ->scheme(...$group->getSchemes())
171
            ->prefix($group->getPath() ?? '')
172
            ->method(...$group->getMethods())
173
        ->middleware(...$group->getMiddlewares());
174
175
        if (null !== $group->getDomain()) {
176
            $route->domain($group->getDomain());
177
        }
178
179
        foreach ($group->getDefaults() as $variable => $default) {
180 4
            $route->default($variable, $default);
181
        }
182 4
183 4
        foreach ($group->getPatterns() as $variable => $regexp) {
184
            $route->assert($variable, $regexp);
185 4
        }
186 4
187
        return $route;
188
    }
189 4
190 3
    /**
191
     * Gets the default route name for a class method.
192 4
     *
193
     * @param class-string|mixed[] $handler
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|mixed[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|mixed[].
Loading history...
194 4
     *
195
     * @return string
196
     */
197
    private function getDefaultRouteName($handler): string
198
    {
199
        $classReflection = new \ReflectionClass(\is_array($handler) ? $handler[0] : $handler);
200
        $name            = \str_replace('\\', '_', $classReflection->name);
201
202
        if (\is_array($handler) || $classReflection->hasMethod('__invoke')) {
203
            $name .= '_' . $handler[1] ?? '__invoke';
204
        }
205
206
        if ($this->defaultRouteIndex > 0) {
207
            $name .= '_' . $this->defaultRouteIndex;
208
        }
209
        ++$this->defaultRouteIndex;
210
211
        return \strtolower($name);
212
    }
213
}
214