Test Failed
Push — master ( f046a9...17fbcb )
by Divine Niiquaye
03:31 queued 01:03
created

RouteCollection::patch()   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 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 1
cts 1
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;
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
     * Inject Groups and sort routes in a natural order.
72
     *
73
     * @return $this
74
     */
75
    final public function buildRoutes(): void
76
    {
77
        $routes = $this->routes;
78
79 13
        if (!empty($this->groups)) {
80
            $this->injectGroups('', $routes);
81 13
        }
82 13
83
        \usort($routes, static function (Route $a, Route $b): int {
84 13
            return !$a->getStaticPrefix() <=> !$b->getStaticPrefix() ?: \strnatcmp($a->getPath(), $b->getPath());
85 1
        });
86 1
87
        $this->uniqueId = null; // Lock grouping and prototyping
88
        $this->routes = $routes;
89 1
    }
90
91 12
    /**
92 12
     * Get all the routes.
93
     *
94
     * @return array<int,Route>
95
     */
96 11
    public function getRoutes(): array
97
    {
98
        if ($this->uniqueId) {
99
            $this->buildRoutes();
100
        }
101
102
        return $this->routes;
103
    }
104 5
105
    /**
106 5
     * Add route to the collection.
107
     */
108
    public function add(Route $route): self
109
    {
110
        $this->routes[] = $this->injectRoute($route);
111
112
        return $this;
113
    }
114
115
    /**
116 81
     * Maps a pattern to a handler.
117
     *
118 81
     * You can must specify HTTP methods that should be matched.
119
     *
120
     * @param string   $pattern Matched route pattern
121
     * @param string[] $methods Matched HTTP methods
122
     * @param mixed    $handler Handler that returns the response when matched
123
     */
124
    public function addRoute(string $pattern, array $methods, $handler = null): Route
125
    {
126
        return $this->routes[] = $this->injectRoute(new Route($pattern, $methods, $handler));
127
    }
128
129
    /**
130 70
     * Add routes to the collection.
131
     *
132 70
     * @param Route[] $routes
133 70
     *
134
     * @throws \TypeError        if $routes doesn't contain a route instance
135 70
     * @throws \RuntimeException if locked
136
     */
137
    public function routes(array $routes): self
138 70
    {
139
        foreach ($routes as $route) {
140
            $this->routes[] = $this->injectRoute($route);
141
        }
142
143
        return $this;
144
    }
145
146
    /**
147
     * Maps a HEAD request to a handler.
148
     *
149
     * @param string $pattern Matched route pattern
150 29
     * @param mixed  $handler Handler that returns the response when matched
151
     */
152 29
    public function head(string $pattern, $handler = null): Route
153
    {
154 29
        return $this->addRoute($pattern, [Router::METHOD_HEAD], $handler);
155
    }
156 29
157
    /**
158
     * Maps a GET and HEAD request to a handler.
159
     *
160
     * @param string $pattern Matched route pattern
161
     * @param mixed  $handler Handler that returns the response when matched
162
     */
163
    public function get(string $pattern, $handler = null): Route
164
    {
165
        return $this->addRoute($pattern, [Router::METHOD_GET, Router::METHOD_HEAD], $handler);
166
    }
167 10
168
    /**
169 10
     * Maps a POST request to a handler.
170 1
     *
171 1
     * @param string $pattern Matched route pattern
172
     * @param mixed  $handler Handler that returns the response when matched
173
     */
174 10
    public function post(string $pattern, $handler = null): Route
175 3
    {
176 3
        return $this->addRoute($pattern, [Router::METHOD_POST], $handler);
177
    }
178 3
179 3
    /**
180 7
     * Maps a PUT request to a handler.
181 1
     *
182
     * @param string $pattern Matched route pattern
183
     * @param mixed  $handler Handler that returns the response when matched
184 9
     */
185
    public function put(string $pattern, $handler = null): Route
186 9
    {
187 9
        return $this->addRoute($pattern, [Router::METHOD_PUT], $handler);
188
    }
189 9
190
    /**
191
     * Maps a PATCH request to a handler.
192
     *
193
     * @param string $pattern Matched route pattern
194
     * @param mixed  $handler Handler that returns the response when matched
195 1
     */
196
    public function patch(string $pattern, $handler = null): Route
197
    {
198 1
        return $this->addRoute($pattern, [Router::METHOD_PATCH], $handler);
199 1
    }
200
201
    /**
202 1
     * Maps a DELETE request to a handler.
203
     *
204
     * @param string $pattern Matched route pattern
205
     * @param mixed  $handler Handler that returns the response when matched
206
     */
207
    public function delete(string $pattern, $handler = null): Route
208
    {
209
        return $this->addRoute($pattern, [Router::METHOD_DELETE], $handler);
210
    }
211 5
212
    /**
213 5
     * Maps a OPTIONS request to a handler.
214
     *
215
     * @param string $pattern Matched route pattern
216
     * @param mixed  $handler Handler that returns the response when matched
217
     */
218
    public function options(string $pattern, $handler = null): Route
219
    {
220
        return $this->addRoute($pattern, [Router::METHOD_OPTIONS], $handler);
221
    }
222 10
223
    /**
224 10
     * Maps any request to a handler.
225
     *
226
     * @param string $pattern Matched route pattern
227
     * @param mixed  $handler Handler that returns the response when matched
228
     */
229
    public function any(string $pattern, $handler = null): Route
230
    {
231
        return $this->addRoute($pattern, Router::HTTP_METHODS_STANDARD, $handler);
232
    }
233 2
234
    /**
235 2
     * Maps any Router::HTTP_METHODS_STANDARD request to a resource handler prefixed to $action's method name.
236
     *
237
     * E.g: Having pattern as "/accounts/{userId}", all request made from supported request methods
238
     * are to have the same url.
239
     *
240
     * @param string              $action   The prefixed name attached to request method
241
     * @param string              $pattern  matched path where request should be sent to
242
     * @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...
243
     */
244 1
    public function resource(string $pattern, $resource, string $action = 'action'): Route
245
    {
246 1
        return $this->any($pattern, new Handlers\ResourceHandler($resource, $action));
247
    }
248
249
    /**
250
     * @throws \RuntimeException if locked
251
     */
252
    protected function injectRoute(Route $route): Route
253
    {
254
        if (null === $this->uniqueId) {
255 2
            throw new \RuntimeException('Adding routes must be done before calling the getRoutes() method.');
256
        }
257 2
258
        foreach ($this->prototypes as $routeMethod => $arguments) {
259
            if (!empty($arguments)) {
260
                if ('prefix' === $routeMethod) {
261
                    $arguments = [\implode('', $arguments)];
262
                }
263
264
                $route->{$routeMethod}(...$arguments);
265
            }
266 1
        }
267
268 1
        if (null !== $this->parent) {
269
            $route->belong($this); // Attach grouping to route.
270
        }
271
272
        return $route;
273
    }
274
}
275