Test Failed
Push — master ( d750d9...412c5f )
by Divine Niiquaye
03:10
created

Listener::getDefaultRouteName()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 8
nc 4
nop 1
dl 0
loc 15
ccs 0
cts 0
cp 0
crap 30
rs 9.6111
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\Interfaces\RouteInterface;
23
use Flight\Routing\Interfaces\RouteListInterface;
24
use Flight\Routing\Route as Router;
25
use Flight\Routing\RouteList;
26
use ReflectionClass;
27
28
class Listener implements ListenerInterface
29
{
30
    /** @var RouteListInterface */
31
    private $collector;
32
33
    /** @var int */
34
    private $defaultRouteIndex = 0;
35
36
    /**
37
     * @param null|RouteList $collector
38 15
     */
39
    public function __construct(?RouteListInterface $collector = null)
40 15
    {
41 15
        $this->collector = $collector ?? new RouteList();
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     *
47
     * @param array<string,array<string,mixed>> $annotations
48 4
     */
49
    public function onAnnotation(array $annotations): RouteListInterface
50 4
    {
51 4
        foreach ($annotations as $class => $collection) {
52 4
            if (isset($collection['method'])) {
53
                $this->addRouteGroup($collection['class'] ?? [], $collection['method']);
54 4
55
                continue;
56
            }
57 3
58
            $this->defaultRouteIndex = 0;
59 3
60 3
            foreach ($collection['class'] ?? [] as $annotation) {
61
                $this->collector->add($this->addRoute($annotation, $class));
62
            }
63 4
        }
64
65
        return $this->collector;
66
    }
67
68
    /**
69 3
     * {@inheritdoc}
70
     */
71 3
    public function getArguments(): array
72
    {
73
        return [];
74
    }
75
76
    /**
77 4
     * {@inheritdoc}
78
     */
79 4
    public function getAnnotation(): string
80
    {
81
        return 'Flight\Routing\Annotation\Route';
82
    }
83
84
    /**
85
     * Add a route from annotation
86
     *
87
     * @param Route           $annotation
88 4
     * @param string|string[] $handler
89
     * @param null|Route      $group
90 4
     *
91 4
     * @return RouteInterface
92
     */
93
    protected function addRoute(Route $annotation, $handler, ?Route $group = null): RouteInterface
94 4
    {
95 3
        if (null === $annotation->getPath()) {
96
            throw new InvalidAnnotationException('@Route.path must not be left empty.');
97
        }
98 4
99 4
        $name    = $annotation->getName() ?? $this->getDefaultRouteName($handler);
100 4
        $methods = str_ends_with($name, '__restful') ? Router::HTTP_METHODS_STANDARD : $annotation->getMethods();
101 4
        $route   = new Router($name, $methods, $annotation->getPath(), $handler);
102 4
103
        $route->setDomain($annotation->getDomain() ?? '')
104 4
            ->setScheme(...$annotation->getSchemes())
105 3
            ->setPatterns($annotation->getPatterns())
106
            ->setDefaults($annotation->getDefaults())
107
        ->addMiddleware(...$annotation->getMiddlewares());
108 4
109
        if (null !== $group) {
110
            $route = $this->mergeGroup($group, $route);
111
        }
112
113
        return $route;
114
    }
115
116
    /**
117 4
     * Add a routes from annotation into group
118
     *
119 4
     * @param Route[] $grouping
120
     * @param mixed[] $methods
121 4
     */
122 4
    protected function addRouteGroup(array $grouping, array $methods): void
123 4
    {
124 4
        if (!empty($grouping)) {
125 4
            foreach ($grouping as $group) {
126 4
                $this->mergeAnnotations($methods, $group);
127 4
            }
128 4
129 4
            return;
130
        }
131
132
        $this->mergeAnnotations($methods);
133 4
    }
134 4
135
    /**
136
     * @param mixed[]    $methods
137
     * @param null|Route $group
138
     */
139
    protected function mergeAnnotations(array $methods, ?Route $group = null): void
140
    {
141 4
        $this->defaultRouteIndex = 0;
142
143 4
        $routes = [];
144
145 4
        foreach ($methods as [$method, $annotation]) {
146
            $routes[] = $this->addRoute($annotation, [$method->class, $method->getName()], $group);
147 4
        }
148 4
149
        $this->collector->addForeach(...$routes);
150
    }
151 4
152
    /**
153
     * @param Route          $group
154
     * @param RouteInterface $route
155
     *
156
     * @return RouteInterface
157
     */
158
    protected function mergeGroup(Route $group, RouteInterface $route): RouteInterface
159
    {
160
        $route = $route->setDomain($group->getDomain() ?? '')
161 4
            ->setName($group->getName() . $route->getName())
162
            ->setScheme(...$group->getSchemes())
163 4
            ->setDefaults($group->getDefaults())
164 4
            ->setPatterns($group->getPatterns())
165
            ->addPrefix($group->getPath() ?? '')
166 4
            ->addMethod(...$group->getMethods())
167 4
        ->addMiddleware(...$group->getMiddlewares());
168
169
        return $route;
170 4
    }
171 3
172
    /**
173 4
     * Gets the default route name for a class method.
174
     *
175 4
     * @param string|string[] $handler
176
     *
177
     * @return string
178
     */
179
    private function getDefaultRouteName($handler): string
180
    {
181
        $classReflection = new ReflectionClass(\is_array($handler) ? $handler[0] : $handler);
182
        $name            = \str_replace('\\', '_', $classReflection->name);
183
184
        if (\is_array($handler) || $classReflection->hasMethod('__invoke')) {
185
            $name .= '_' . $handler[1] ?? '__invoke';
186
        }
187
188
        if ($this->defaultRouteIndex > 0) {
189
            $name .= '_' . $this->defaultRouteIndex;
190
        }
191
        ++$this->defaultRouteIndex;
192
193
        return \strtolower($name);
194
    }
195
}
196