Test Failed
Push — master ( fd3819...5bc6ef )
by Divine Niiquaye
13:04
created

RouteCollection::__call()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 5
eloc 11
nc 3
nop 2
dl 0
loc 21
ccs 4
cts 4
cp 1
crap 5
rs 9.6111
c 3
b 1
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A RouteCollection::addRoute() 0 3 1
A RouteCollection::add() 0 5 1
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;
19
20
/**
21
 * A RouteCollection represents a set of Route instances.
22
 *
23
 * This class provides all(*) methods for creating path+HTTP method-based routes and
24
 * injecting them into the router:
25
 *
26
 * - head
27
 * - get
28
 * - post
29
 * - put
30
 * - patch
31
 * - delete
32
 * - options
33
 * - any
34
 * - resource
35
 *
36
 * A general `addRoute()` method allows specifying multiple request methods and/or
37
 * arbitrary request methods when creating a path-based route.
38
 *
39
 * @author Divine Niiquaye Ibok <[email protected]>
40
 */
41
class RouteCollection
42
{
43
    use Traits\PrototypeTrait;
44
    use Traits\GroupingTrait;
45
46
    private ?self $parent = null;
47
48
    /** @var array<int,Route> */
49
    private array $routes = [];
50
51
    /**
52
     * @param string $namedPrefix The unqiue name for this group
53
     */
54
    public function __construct(string $namedPrefix = '')
55
    {
56
        $this->namedPrefix = $namedPrefix;
57
        $this->uniqueId = \uniqid($namedPrefix);
58
    }
59
60
    /**
61
     * Nested collection and routes should be cloned.
62
     */
63
    public function __clone()
64
    {
65
        foreach ($this->routes as $offset => $route) {
66
            $this->routes[$offset] = clone $route;
67
        }
68
    }
69
70
    /**
71
     * Get all the routes.
72
     *
73
     * @return array<int,Route>
74
     */
75
    public function getRoutes(): array
76
    {
77
        $routes = $this->routes;
78
79 13
        if (!$this->uniqueId) {
80
            return $routes;
81 13
        }
82 13
83
        if (!empty($this->groups)) {
84 13
            $this->injectGroups('', $routes);
85 1
        }
86 1
87
        $this->uniqueId = null; // Lock grouping and prototyping
88
        \usort($routes, static function (Route $a, Route $b): int {
89 1
            return !$a->getStaticPrefix() <=> !$b->getStaticPrefix() ?: \strnatcmp($a->getPath(), $b->getPath());
90
        });
91 12
92 12
        return $this->routes = $routes;
93
    }
94
95
    /**
96 11
     * Add route to the collection.
97
     */
98
    public function add(Route $route): self
99
    {
100
        $this->routes[] = $this->injectRoute($route);
101
102
        return $this;
103
    }
104 5
105
    /**
106 5
     * Maps a pattern to a handler.
107
     *
108
     * You can must specify HTTP methods that should be matched.
109
     *
110
     * @param string   $pattern Matched route pattern
111
     * @param string[] $methods Matched HTTP methods
112
     * @param mixed    $handler Handler that returns the response when matched
113
     */
114
    public function addRoute(string $pattern, array $methods, $handler = null): Route
115
    {
116 81
        return $this->routes[] = $this->injectRoute(new Route($pattern, $methods, $handler));
117
    }
118 81
119
    /**
120
     * Add routes to the collection.
121
     *
122
     * @param Route[] $routes
123
     *
124
     * @throws \TypeError        if $routes doesn't contain a route instance
125
     * @throws \RuntimeException if locked
126
     */
127
    public function routes(array $routes): self
128
    {
129
        foreach ($routes as $route) {
130 70
            $this->routes[] = $this->injectRoute($route);
131
        }
132 70
133 70
        return $this;
134
    }
135 70
136
    /**
137
     * Maps a HEAD request to a handler.
138 70
     *
139
     * @param string $pattern Matched route pattern
140
     * @param mixed  $handler Handler that returns the response when matched
141
     */
142
    public function head(string $pattern, $handler = null): Route
143
    {
144
        return $this->addRoute($pattern, [Router::METHOD_HEAD], $handler);
145
    }
146
147
    /**
148
     * Maps a GET and HEAD request to a handler.
149
     *
150 29
     * @param string $pattern Matched route pattern
151
     * @param mixed  $handler Handler that returns the response when matched
152 29
     */
153
    public function get(string $pattern, $handler = null): Route
154 29
    {
155
        return $this->addRoute($pattern, [Router::METHOD_GET, Router::METHOD_HEAD], $handler);
156 29
    }
157
158
    /**
159
     * Maps a POST request to a handler.
160
     *
161
     * @param string $pattern Matched route pattern
162
     * @param mixed  $handler Handler that returns the response when matched
163
     */
164
    public function post(string $pattern, $handler = null): Route
165
    {
166
        return $this->addRoute($pattern, [Router::METHOD_POST], $handler);
167 10
    }
168
169 10
    /**
170 1
     * Maps a PUT request to a handler.
171 1
     *
172
     * @param string $pattern Matched route pattern
173
     * @param mixed  $handler Handler that returns the response when matched
174 10
     */
175 3
    public function put(string $pattern, $handler = null): Route
176 3
    {
177
        return $this->addRoute($pattern, [Router::METHOD_PUT], $handler);
178 3
    }
179 3
180 7
    /**
181 1
     * Maps a PATCH request to a handler.
182
     *
183
     * @param string $pattern Matched route pattern
184 9
     * @param mixed  $handler Handler that returns the response when matched
185
     */
186 9
    public function patch(string $pattern, $handler = null): Route
187 9
    {
188
        return $this->addRoute($pattern, [Router::METHOD_PATCH], $handler);
189 9
    }
190
191
    /**
192
     * Maps a DELETE request to a handler.
193
     *
194
     * @param string $pattern Matched route pattern
195 1
     * @param mixed  $handler Handler that returns the response when matched
196
     */
197
    public function delete(string $pattern, $handler = null): Route
198 1
    {
199 1
        return $this->addRoute($pattern, [Router::METHOD_DELETE], $handler);
200
    }
201
202 1
    /**
203
     * Maps a OPTIONS request to a handler.
204
     *
205
     * @param string $pattern Matched route pattern
206
     * @param mixed  $handler Handler that returns the response when matched
207
     */
208
    public function options(string $pattern, $handler = null): Route
209
    {
210
        return $this->addRoute($pattern, [Router::METHOD_OPTIONS], $handler);
211 5
    }
212
213 5
    /**
214
     * Maps any request to a handler.
215
     *
216
     * @param string $pattern Matched route pattern
217
     * @param mixed  $handler Handler that returns the response when matched
218
     */
219
    public function any(string $pattern, $handler = null): Route
220
    {
221
        return $this->addRoute($pattern, Router::HTTP_METHODS_STANDARD, $handler);
222 10
    }
223
224 10
    /**
225
     * Maps any Router::HTTP_METHODS_STANDARD request to a resource handler prefixed to $action's method name.
226
     *
227
     * E.g: Having pattern as "/accounts/{userId}", all request made from supported request methods
228
     * are to have the same url.
229
     *
230
     * @param string              $action   The prefixed name attached to request method
231
     * @param string              $pattern  matched path where request should be sent to
232
     * @param class-string|object $resource Handler that returns the response
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|object at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|object.
Loading history...
233 2
     */
234
    public function resource(string $pattern, $resource, string $action = 'action'): Route
235 2
    {
236
        return $this->any($pattern, new Handlers\ResourceHandler($resource, $action));
237
    }
238
239
    /**
240
     * @throws \RuntimeException if locked
241
     */
242
    protected function injectRoute(Route $route): Route
243
    {
244 1
        if (null === $this->uniqueId) {
245
            throw new \RuntimeException('Adding routes must be done before calling the getRoutes() method.');
246 1
        }
247
248
        foreach ($this->prototypes as $routeMethod => $arguments) {
249
            if (!empty($arguments)) {
250
                if ('prefix' === $routeMethod) {
251
                    $arguments = [\implode('', $arguments)];
252
                }
253
254
                $route->{$routeMethod}(...$arguments);
255 2
            }
256
        }
257 2
258
        if (null !== $this->parent) {
259
            $route->belong($this); // Attach grouping to route.
260
        }
261
262
        return $route;
263
    }
264
}
265