Test Failed
Pull Request — master (#16)
by Divine Niiquaye
03:04
created

RouteCollection::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 2
b 0
f 0
nc 2
nop 2
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 6
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.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, \Countable
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 a SplFixedArray that includes all routes.
95
     *
96 11
     * @see doMerge() method
97
     *
98
     * @return \SplFixedArray<int,Route> The filtered routes
99
     */
100
    public function getIterator(): \SplFixedArray
101
    {
102
        if ($this->routes instanceof \SplFixedArray && !$this->hasGroups) {
103
            return $this->routes;
104 5
        }
105
106 5
        return $this->routes = $this->doMerge('', new static($this->compiler, null !== $this->profiler));
107
    }
108
109
    /**
110
     * Count all routes in the collection.
111
     */
112
    public function count(): int
113
    {
114
        return $this->countRoutes;
115
    }
116 81
117
    /**
118 81
     * Add route(s) to the collection.
119
     *
120
     * This method unset all setting from default route and use new settings
121
     * from new the route(s). If you want the default settings to be merged
122
     * into routes, use `addRoute` method instead.
123
     *
124
     * @param Route ...$routes
125
     */
126
    public function add(Route ...$routes): self
127
    {
128
        foreach ($routes as $route) {
129
            $this->routes[] = $this->resolveWith($route);
130 70
        }
131
132 70
        return $this;
133 70
    }
134
135 70
    /**
136
     * Maps a pattern to a handler.
137
     *
138 70
     * You can must specify HTTP methods that should be matched.
139
     *
140
     * @param string   $pattern Matched route pattern
141
     * @param string[] $methods Matched HTTP methods
142
     * @param mixed    $handler Handler that returns the response when matched
143
     */
144
    public function addRoute(string $pattern, array $methods, $handler = null): Route
145
    {
146
        return $this->routes[] = $this->resolveWith(new Route($pattern, $methods, $handler));
147
    }
148
149
    /**
150 29
     * Mounts controllers under the given route prefix.
151
     *
152 29
     * @param string                   $name        The route group prefixed name
153
     * @param callable|RouteCollection $controllers A RouteCollection instance or a callable for defining routes
154 29
     *
155
     * @throws \LogicException
156 29
     */
157
    public function group(string $name, $controllers = null): self
158
    {
159
        if (null === $controllers) {
160
            $controllers = new static();
161
            $controllers->stack = $this->stack ?? [];
162
        } elseif (\is_callable($controllers)) {
163
            $controllers($controllers = new static());
164
        }
165
166
        if (!$controllers instanceof self) {
167 10
            throw new \LogicException(\sprintf('The %s() method takes either a "%s" instance or a callable of its self.', __METHOD__, __CLASS__));
168
        }
169 10
170 1
        if (!empty($name)) {
171 1
            $this->routes[$name] = $controllers;
172
        } else {
173
            $this->routes[] = $controllers;
174 10
        }
175 3
176 3
        $controllers->parent = $this;
177
        $this->hasGroups = true;
178 3
179 3
        return $controllers;
180 7
    }
181 1
182
    /**
183
     * Unmounts a group collection to continue routes stalk.
184 9
     */
185
    public function end(): self
186 9
    {
187 9
        // Remove last element from stack.
188
        if (null !== $stack = $this->stack) {
189 9
            unset($stack[\count($stack) - 1]);
190
        }
191
192
        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...
193
    }
194
195 1
    /**
196
     * Maps a HEAD request to a handler.
197
     *
198 1
     * @param string $pattern Matched route pattern
199 1
     * @param mixed  $handler Handler that returns the response when matched
200
     */
201
    public function head(string $pattern, $handler = null): Route
202 1
    {
203
        return $this->addRoute($pattern, [Router::METHOD_HEAD], $handler);
204
    }
205
206
    /**
207
     * Maps a GET and HEAD request to a handler.
208
     *
209
     * @param string $pattern Matched route pattern
210
     * @param mixed  $handler Handler that returns the response when matched
211 5
     */
212
    public function get(string $pattern, $handler = null): Route
213 5
    {
214
        return $this->addRoute($pattern, [Router::METHOD_GET, Router::METHOD_HEAD], $handler);
215
    }
216
217
    /**
218
     * Maps a POST request to a handler.
219
     *
220
     * @param string $pattern Matched route pattern
221
     * @param mixed  $handler Handler that returns the response when matched
222 10
     */
223
    public function post(string $pattern, $handler = null): Route
224 10
    {
225
        return $this->addRoute($pattern, [Router::METHOD_POST], $handler);
226
    }
227
228
    /**
229
     * Maps a PUT request to a handler.
230
     *
231
     * @param string $pattern Matched route pattern
232
     * @param mixed  $handler Handler that returns the response when matched
233 2
     */
234
    public function put(string $pattern, $handler = null): Route
235 2
    {
236
        return $this->addRoute($pattern, [Router::METHOD_PUT], $handler);
237
    }
238
239
    /**
240
     * Maps a PATCH request to a handler.
241
     *
242
     * @param string $pattern Matched route pattern
243
     * @param mixed  $handler Handler that returns the response when matched
244 1
     */
245
    public function patch(string $pattern, $handler = null): Route
246 1
    {
247
        return $this->addRoute($pattern, [Router::METHOD_PATCH], $handler);
248
    }
249
250
    /**
251
     * Maps a DELETE request to a handler.
252
     *
253
     * @param string $pattern Matched route pattern
254
     * @param mixed  $handler Handler that returns the response when matched
255 2
     */
256
    public function delete(string $pattern, $handler = null): Route
257 2
    {
258
        return $this->addRoute($pattern, [Router::METHOD_DELETE], $handler);
259
    }
260
261
    /**
262
     * Maps a OPTIONS request to a handler.
263
     *
264
     * @param string $pattern Matched route pattern
265
     * @param mixed  $handler Handler that returns the response when matched
266 1
     */
267
    public function options(string $pattern, $handler = null): Route
268 1
    {
269
        return $this->addRoute($pattern, [Router::METHOD_OPTIONS], $handler);
270
    }
271
272
    /**
273
     * Maps any request to a handler.
274
     *
275
     * @param string $pattern Matched route pattern
276
     * @param mixed  $handler Handler that returns the response when matched
277 1
     */
278
    public function any(string $pattern, $handler = null): Route
279 1
    {
280
        return $this->addRoute($pattern, Router::HTTP_METHODS_STANDARD, $handler);
281
    }
282
283
    /**
284
     * Maps any Router::HTTP_METHODS_STANDARD request to a resource handler prefixed to $action's method name.
285
     *
286
     * E.g: Having pattern as "/accounts/{userId}", all request made from supported request methods
287
     * are to have the same url.
288 3
     *
289
     * @param string              $action   The prefixed name attached to request method
290 3
     * @param string              $pattern  matched path where request should be sent to
291
     * @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...
292
     */
293
    public function resource(string $pattern, $resource, string $action = 'action'): Route
294
    {
295
        return $this->any($pattern, new Handlers\ResourceHandler($resource, $action));
296
    }
297
298
    /**
299
     * Find a route by name.
300
     *
301
     * @param string $name The route name
302 2
     *
303
     * @return Route|null A Route instance or null when not found
304 2
     */
305
    public function find(string $name): ?Route
306
    {
307
        foreach ($this->routes as $route) {
308
            if ($route instanceof Route && $name === $route->get('name')) {
309
                return $route;
310
            }
311
        }
312
313
        return null;
314 71
    }
315
}
316