Test Failed
Pull Request — master (#16)
by Divine Niiquaye
10:53
created

Listener::load()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
nc 4
nop 1
dl 0
loc 24
ccs 8
cts 8
cp 1
crap 5
rs 9.5555
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 Biurad\Annotations\Locate\Class_;
23
use Flight\Routing\{Routes\DomainRoute, RouteCollection};
24
25
class Listener implements ListenerInterface
26
{
27
    /** @var RouteCollection */
28
    private $collector;
29
30
    /** @var string|null */
31
    private $unNamedPrefix;
32
33
    /** @var array<string,int> */
34 11
    private $defaultUnnamedIndex = [];
35
36 11
    /**
37 11
     * @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
    public function __construct(RouteCollection $collector = null, ?string $unNamedPrefix = '')
41
    {
42 6
        $this->unNamedPrefix = $unNamedPrefix;
43
        $this->collector = $collector ?? new RouteCollection();
44 6
    }
45 6
46 4
    /**
47 4
     * {@inheritdoc}
48 4
     */
49 4
    public function load(array $annotations): RouteCollection
50
    {
51
        $foundAnnotations = [];
52 4
53
        foreach ($annotations as $annotation) {
54
            if ($annotation instanceof Class_) {
55 3
                $methodAnnotations = [];
56
57
                foreach ($annotation->methods as $method) {
58 4
                    $controller = [$method->getReflection()->class, (string) $method];
59
                    $this->getRoutes($method->getAnnotation(), $controller, $methodAnnotations);
60
                }
61 5
62
                if (!empty($methodAnnotations)) {
63
                    $this->addRoute($annotation->getAnnotation(), $methodAnnotations, $foundAnnotations);
64 4
65
                    continue;
66
                }
67
            }
68
69
            $this->getRoutes($annotation->getAnnotation(), (string) $annotation, $foundAnnotations, $annotation instanceof Class_);
70 11
        }
71
72 11
        return $this->collector->add(...$foundAnnotations);
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function getAnnotation(): string
79
    {
80 6
        return 'Flight\Routing\Annotation\Route';
81
    }
82 6
83
    /**
84 6
     * Add a route from class annotated methods.
85 4
     *
86
     * @param iterable<Route> $classAnnotations
87
     * @param DomainRoute[]   $methodAnnotations
88
     * @param DomainRoute[]   $foundAnnotations
89 6
     */
90 6
    protected function addRoute(iterable $classAnnotations, array $methodAnnotations, array &$foundAnnotations): void
91 4
    {
92
        foreach ($methodAnnotations as $methodAnnotation) {
93
            if (!empty($classAnnotations)) {
94 4
                foreach ($classAnnotations as $classAnnotation) {
95
                    if (null !== $classAnnotation->resource) {
96
                        throw new InvalidAnnotationException('Restful annotated class cannot contain annotated method(s).');
97 6
                    }
98 2
99
                    $annotatedMethod = clone $methodAnnotation->method(...$classAnnotation->methods)
100
                        ->scheme(...$classAnnotation->schemes)
101 4
                        ->domain(...$classAnnotation->hosts)
102 4
                        ->defaults($classAnnotation->defaults)
103 4
                        ->asserts($classAnnotation->patterns);
104 4
105 4
                    if (null !== $classAnnotation->path) {
106 4
                        $annotatedMethod->prefix($classAnnotation->path);
107 4
                    }
108 4
109
                    if (!empty($routeName = $this->resolveRouteName($classAnnotation->name, $annotatedMethod, true))) {
110 4
                        $annotatedMethod->bind($routeName);
111
                    }
112 4
113 4
                    $foundAnnotations[] = $annotatedMethod;
114
                }
115 4
116 3
                continue;
117
            }
118
119
            if (!empty($routeName = $this->resolveRouteName(null, $methodAnnotation))) {
120 4
                $methodAnnotation->bind($routeName);
121 4
            }
122
123
            $foundAnnotations[] = $methodAnnotation;
124 4
        }
125
    }
126 4
127
    /**
128
     * @param iterable<Route> $annotations
129
     * @param mixed           $handler
130
     * @param DomainRoute[]   $foundAnnotations
131
     */
132
    protected function getRoutes(iterable $annotations, $handler, array &$foundAnnotations, bool $single = false): void
133
    {
134
        foreach ($annotations as $annotation) {
135
            if (!$single && null !== $annotation->resource) {
136
                throw new InvalidAnnotationException('Restful annotation is only supported on classes.');
137
            }
138
139
            $route = $annotation->getRoute($handler);
140
141
            if ($single && $routeName = $this->resolveRouteName(null, $route)) {
142
                $route->bind($routeName);
143
            }
144
145
            $foundAnnotations[] = $route;
146
        }
147
    }
148
149
    /**
150
     * Resolve route naming.
151
     */
152
    private function resolveRouteName(?string $prefix, DomainRoute $route, bool $force = false): ?string
153
    {
154
        $name = $route->get('name');
155
156
        if ((null !== $this->unNamedPrefix || $force) && empty($name)) {
157
            $name = $base = $prefix . $route->generateRouteName($this->unNamedPrefix ?? '');
158
159
            if (isset($this->defaultUnnamedIndex[$name])) {
160
                $name = $base . '_' . ++$this->defaultUnnamedIndex[$name];
161
            } else {
162
                $this->defaultUnnamedIndex[$name] = 0;
163
            }
164
165
            return $name;
166
        }
167
168
        return $prefix . $name;
0 ignored issues
show
Bug introduced by
Are you sure $name of type array|mixed|null can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

168
        return $prefix . /** @scrutinizer ignore-type */ $name;
Loading history...
169
    }
170
}
171