Test Failed
Pull Request — master (#16)
by Divine Niiquaye
02:47
created

RouteCollection::patch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 2
crap 2
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;
19
20
use Flight\Routing\Interfaces\RouteCompilerInterface;
21
22
/**
23
 * A RouteCollection represents a set of Route instances.
24
 *
25
 * This class provides all(*) methods for creating path+HTTP method-based routes and
26
 * injecting them into the router:
27
 *
28
 * - head
29
 * - get
30
 * - post
31
 * - put
32
 * - patch
33
 * - delete
34
 * - options
35
 * - any
36
 * - resource
37
 *
38
 * A general `addRoute()` method allows specifying multiple request methods and/or
39
 * arbitrary request methods when creating a path-based route.
40
 *
41
 * @method RouteCollection withAssert(string $variable, string|string[] $regexp)
42
 * @method RouteCollection withDefault(string $variable, mixed $default)
43
 * @method RouteCollection withArgument($variable, mixed $value)
44
 * @method RouteCollection withMethod(string ...$methods)
45
 * @method RouteCollection withScheme(string ...$schemes)
46
 * @method RouteCollection withMiddleware(MiddlewareInterface ...$middlewares)
47
 * @method RouteCollection withDomain(string ...$hosts)
48
 * @method RouteCollection withPrefix(string $path)
49
 * @method RouteCollection withDefaults(array $values)
50
 * @method RouteCollection withAsserts(array $patterns)
51
 * @method RouteCollection withArguments(array $patterns)
52
 * @method RouteCollection withNamespace(string $namespace)
53
 *
54
 * @author Divine Niiquaye Ibok <[email protected]>
55
 */
56
final class RouteCollection implements \IteratorAggregate
57
{
58
    use Traits\GroupingTrait;
59
60
    public function __construct(RouteCompilerInterface $compiler = null, bool $debug = false)
61
    {
62
        $this->compiler = $compiler ?? new RouteCompiler();
63
        $this->profiler = $debug ? new DebugRoute() : null;
64
    }
65
66
    /**
67
     * @param string   $method
68
     * @param string[] $arguments
69
     *
70
     * @return mixed
71
     */
72
    public function __call($method, $arguments)
73
    {
74
        $routeMethod = (string) \preg_replace('/^with([A-Z]{1}[a-z]+)$/', '\1', $method, 1);
75
        $routeMethod = \strtolower($routeMethod);
76
77
        if (null !== $stack = $this->stack) {
78
            $this->stack[$routeMethod] = \array_merge($stack[$routeMethod] ?? [], $arguments);
79 13
        } else {
80
            foreach ($this->routes as $route) {
81 13
                \call_user_func_array([$route, $routeMethod], $arguments);
82 13
            }
83
        }
84 13
85 1
        return $this;
86 1
    }
87
88
    public function getCompiler(): RouteCompilerInterface
89 1
    {
90
        return $this->compiler;
91 12
    }
92 12
93
    /**
94
     * Gets the filtered RouteCollection as an ArrayIterator that includes all routes.
95
     *
96 11
     * @see doMerge() method
97
     *
98
     * @return \ArrayIterator<int,Route> The filtered routes
99
     */
100
    public function getIterator(): \ArrayIterator
101
    {
102
        if ($this->routes instanceof \ArrayIterator && !$this->hasGroups) {
103
            return $this->routes;
104 5
        }
105
106 5
        return $this->routes = $this->doMerge('', 0, new \ArrayIterator());
107
    }
108
109
    /**
110
     * Add route(s) to the collection.
111
     *
112
     * This method unset all setting from default route and use new settings
113
     * from new the route(s). If you want the default settings to be merged
114
     * into routes, use `addRoute` method instead.
115
     *
116 81
     * @param Route ...$routes
117
     */
118 81
    public function add(Route ...$routes): self
119
    {
120
        foreach ($routes as $route) {
121
            $this->routes[] = $this->resolveWith($route);
122
        }
123
124
        return $this;
125
    }
126
127
    /**
128
     * Maps a pattern to a handler.
129
     *
130 70
     * You can must specify HTTP methods that should be matched.
131
     *
132 70
     * @param string   $pattern Matched route pattern
133 70
     * @param string[] $methods Matched HTTP methods
134
     * @param mixed    $handler Handler that returns the response when matched
135 70
     */
136
    public function addRoute(string $pattern, array $methods, $handler = null): Route
137
    {
138 70
        return $this->routes[] = $this->resolveWith(new Route($pattern, $methods, $handler));
139
    }
140
141
    /**
142
     * Mounts controllers under the given route prefix.
143
     *
144
     * @param string                   $name        The route group prefixed name
145
     * @param callable|RouteCollection $controllers A RouteCollection instance or a callable for defining routes
146
     *
147
     * @throws \LogicException
148
     */
149
    public function group(string $name, $controllers = null): self
150 29
    {
151
        if (null === $controllers) {
152 29
            $controllers = new static();
153
            $controllers->stack = $this->stack ?? [];
154 29
        } elseif (\is_callable($controllers)) {
155
            $controllers($controllers = new static());
156 29
        }
157
158
        if (!$controllers instanceof self) {
159
            throw new \LogicException(\sprintf('The %s() method takes either a "%s" instance or a callable of its self.', __METHOD__, __CLASS__));
160
        }
161
162
        if (!empty($name)) {
163
            $this->routes[$name] = $controllers;
164
        } else {
165
            $this->routes[] = $controllers;
166
        }
167 10
168
        $controllers->parent = $this;
169 10
        $this->hasGroups = true;
170 1
171 1
        return $controllers;
172
    }
173
174 10
    /**
175 3
     * Unmounts a group collection to continue routes stalk.
176 3
     */
177
    public function end(): self
178 3
    {
179 3
        // Remove last element from stack.
180 7
        if (null !== $stack = $this->stack) {
181 1
            unset($stack[\count($stack) - 1]);
182
        }
183
184 9
        return $this->parent ?? $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parent ?? $this could return the type Flight\Routing\Traits\GroupingTrait which includes types incompatible with the type-hinted return Flight\Routing\RouteCollection. Consider adding an additional type-check to rule them out.
Loading history...
185
    }
186 9
187 9
    /**
188
     * Maps a HEAD request to a handler.
189 9
     *
190
     * @param string $pattern Matched route pattern
191
     * @param mixed  $handler Handler that returns the response when matched
192
     */
193
    public function head(string $pattern, $handler = null): Route
194
    {
195 1
        return $this->addRoute($pattern, [Router::METHOD_HEAD], $handler);
196
    }
197
198 1
    /**
199 1
     * Maps a GET and HEAD request to a handler.
200
     *
201
     * @param string $pattern Matched route pattern
202 1
     * @param mixed  $handler Handler that returns the response when matched
203
     */
204
    public function get(string $pattern, $handler = null): Route
205
    {
206
        return $this->addRoute($pattern, [Router::METHOD_GET, Router::METHOD_HEAD], $handler);
207
    }
208
209
    /**
210
     * Maps a POST request to a handler.
211 5
     *
212
     * @param string $pattern Matched route pattern
213 5
     * @param mixed  $handler Handler that returns the response when matched
214
     */
215
    public function post(string $pattern, $handler = null): Route
216
    {
217
        return $this->addRoute($pattern, [Router::METHOD_POST], $handler);
218
    }
219
220
    /**
221
     * Maps a PUT request to a handler.
222 10
     *
223
     * @param string $pattern Matched route pattern
224 10
     * @param mixed  $handler Handler that returns the response when matched
225
     */
226
    public function put(string $pattern, $handler = null): Route
227
    {
228
        return $this->addRoute($pattern, [Router::METHOD_PUT], $handler);
229
    }
230
231
    /**
232
     * Maps a PATCH request to a handler.
233 2
     *
234
     * @param string $pattern Matched route pattern
235 2
     * @param mixed  $handler Handler that returns the response when matched
236
     */
237
    public function patch(string $pattern, $handler = null): Route
238
    {
239
        return $this->addRoute($pattern, [Router::METHOD_PATCH], $handler);
240
    }
241
242
    /**
243
     * Maps a DELETE request to a handler.
244 1
     *
245
     * @param string $pattern Matched route pattern
246 1
     * @param mixed  $handler Handler that returns the response when matched
247
     */
248
    public function delete(string $pattern, $handler = null): Route
249
    {
250
        return $this->addRoute($pattern, [Router::METHOD_DELETE], $handler);
251
    }
252
253
    /**
254
     * Maps a OPTIONS request to a handler.
255 2
     *
256
     * @param string $pattern Matched route pattern
257 2
     * @param mixed  $handler Handler that returns the response when matched
258
     */
259
    public function options(string $pattern, $handler = null): Route
260
    {
261
        return $this->addRoute($pattern, [Router::METHOD_OPTIONS], $handler);
262
    }
263
264
    /**
265
     * Maps any request to a handler.
266 1
     *
267
     * @param string $pattern Matched route pattern
268 1
     * @param mixed  $handler Handler that returns the response when matched
269
     */
270
    public function any(string $pattern, $handler = null): Route
271
    {
272
        return $this->addRoute($pattern, Router::HTTP_METHODS_STANDARD, $handler);
273
    }
274
275
    /**
276
     * Maps any Router::HTTP_METHODS_STANDARD request to a resource handler prefixed to $action's method name.
277 1
     *
278
     * E.g: Having pattern as "/accounts/{userId}", all request made from supported request methods
279 1
     * are to have the same url.
280
     *
281
     * @param string              $action   The prefixed name attached to request method
282
     * @param string              $pattern  matched path where request should be sent to
283
     * @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...
284
     */
285
    public function resource(string $pattern, $resource, string $action = 'action'): Route
286
    {
287
        return $this->any($pattern, new Handlers\ResourceHandler($resource, $action));
288 3
    }
289
290 3
    /**
291
     * Find a route by name.
292
     *
293
     * @param string $name The route name
294
     *
295
     * @return Route|null A Route instance or null when not found
296
     */
297
    public function find(string $name): ?Route
298
    {
299
        foreach ($this->routes as $route) {
300
            if ($route instanceof Route && $name === $route->get('name')) {
301
                return $route;
302 2
            }
303
        }
304 2
305
        return null;
306
    }
307
}
308