Passed
Push — master ( 23a26d...cc7dc4 )
by Melech
04:05
created

Collection::getRouteByName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Http\Routing\Collection;
15
16
use JsonException;
17
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Valkyrja\Http\Message\Enum\RequestMethod;
0 ignored issues
show
Bug introduced by
The type Valkyrja\Http\Message\Enum\RequestMethod was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Valkyrja\Http\Routing\Collection\Contract\Collection as Contract;
20
use Valkyrja\Http\Routing\Data;
21
use Valkyrja\Http\Routing\Data\Contract\Route;
22
use Valkyrja\Http\Routing\Exception\InvalidArgumentException;
23
24
use function array_map;
25
use function array_merge;
26
use function assert;
27
use function is_string;
28
29
/**
30
 * Class Collection.
31
 *
32
 * @author Melech Mizrachi
33
 *
34
 * @phpstan-import-type RequestMethodList from Contract
35
 * @phpstan-import-type RequestMethodRouteList from Contract
36
 *
37
 * @psalm-import-type RequestMethodList from Contract
38
 * @psalm-import-type RequestMethodRouteList from Contract
39
 */
40
class Collection implements Contract
41
{
42
    /**
43
     * The routes.
44
     *
45
     * @var array<string, Route|string>
46
     */
47
    protected array $routes = [];
48
49
    /**
50
     * The static routes.
51
     *
52
     * @var RequestMethodList
0 ignored issues
show
Bug introduced by
The type Valkyrja\Http\Routing\Collection\RequestMethodList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
53
     */
54
    protected array $static = [];
55
56
    /**
57
     * The dynamic routes.
58
     *
59
     * @var RequestMethodList
60
     */
61
    protected array $dynamic = [];
62
63
    /**
64
     * @inheritDoc
65
     */
66
    #[Override]
67
    public function getData(): Data
68
    {
69
        return new Data(
70
            routes: array_map('serialize', $this->routes),
71
            static: $this->static,
72
            dynamic: $this->dynamic,
73
        );
74
    }
75
76
    /**
77
     * @inheritDoc
78
     */
79
    #[Override]
80
    public function setFromData(Data $data): void
81
    {
82
        $this->routes  = $data->routes;
83
        $this->static  = $data->static;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data->static of type array is incompatible with the declared type Valkyrja\Http\Routing\Collection\RequestMethodList of property $static.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Documentation Bug introduced by
It seems like $data->static of type array is incompatible with the declared type Valkyrja\Http\Routing\RequestArray of property $static.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
84
        $this->dynamic = $data->dynamic;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data->dynamic of type array is incompatible with the declared type Valkyrja\Http\Routing\RequestArray of property $dynamic.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Documentation Bug introduced by
It seems like $data->dynamic of type array is incompatible with the declared type Valkyrja\Http\Routing\Collection\RequestMethodList of property $dynamic.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    #[Override]
91
    public function add(Route $route): void
92
    {
93
        assert($route->getPath());
94
95
        // Set the route to its request methods
96
        $this->setRouteToRequestMethods($route);
97
98
        $this->routes[$route->getName()] = $route;
99
    }
100
101
    /**
102
     * @inheritDoc
103
     *
104
     * @throws JsonException
105
     */
106
    #[Override]
107
    public function get(string $path, RequestMethod|null $method = null): Route|null
108
    {
109
        return $this->getStatic($path, $method) ?? $this->getDynamic($path, $method);
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115
    #[Override]
116
    public function isset(string $path, RequestMethod|null $method = null): bool
117
    {
118
        return $this->hasStatic($path, $method) || $this->hasDynamic($path, $method);
119
    }
120
121
    /**
122
     * @inheritDoc
123
     */
124
    #[Override]
125
    public function all(): array
126
    {
127
        return $this->ensureMethodRoutes(array_merge($this->static, $this->dynamic));
128
    }
129
130
    /**
131
     * @inheritDoc
132
     */
133
    #[Override]
134
    public function allFlattened(): array
135
    {
136
        return $this->ensureRoutes($this->routes);
137
    }
138
139
    /**
140
     * @inheritDoc
141
     *
142
     * @throws JsonException
143
     */
144
    #[Override]
145
    public function getStatic(string $path, RequestMethod|null $method = null): Route|null
146
    {
147
        return $this->getOfType($this->static, $path, $method);
148
    }
149
150
    /**
151
     * @inheritDoc
152
     */
153
    #[Override]
154
    public function hasStatic(string $path, RequestMethod|null $method = null): bool
155
    {
156
        return $this->hasOfType($this->static, $path, $method);
157
    }
158
159
    /**
160
     * @inheritDoc
161
     */
162
    #[Override]
163
    public function allStatic(RequestMethod|null $method = null): array
164
    {
165
        return $this->allOfType($this->static, $method);
166
    }
167
168
    /**
169
     * @inheritDoc
170
     *
171
     * @throws JsonException
172
     */
173
    #[Override]
174
    public function getDynamic(string $regex, RequestMethod|null $method = null): Route|null
175
    {
176
        return $this->getOfType($this->dynamic, $regex, $method);
177
    }
178
179
    /**
180
     * @inheritDoc
181
     */
182
    #[Override]
183
    public function hasDynamic(string $regex, RequestMethod|null $method = null): bool
184
    {
185
        return $this->hasOfType($this->dynamic, $regex, $method);
186
    }
187
188
    /**
189
     * @inheritDoc
190
     */
191
    #[Override]
192
    public function allDynamic(RequestMethod|null $method = null): array
193
    {
194
        return $this->allOfType($this->dynamic, $method);
195
    }
196
197
    /**
198
     * @inheritDoc
199
     */
200
    #[Override]
201
    public function getRouteByName(string $name): Route|null
202
    {
203
        $named = $this->routes[$name] ?? null;
204
205
        if ($named === null) {
206
            return null;
207
        }
208
209
        return $this->ensureRoute($named);
210
    }
211
212
    /**
213
     * @inheritDoc
214
     */
215
    #[Override]
216
    public function hasNamed(string $name): bool
217
    {
218
        return isset($this->routes[$name]);
219
    }
220
221
    /**
222
     * Set a route to its request methods.
223
     *
224
     * @param Route $route The route
225
     *
226
     * @return void
227
     */
228
    protected function setRouteToRequestMethods(Route $route): void
229
    {
230
        foreach ($route->getRequestMethods() as $requestMethod) {
231
            $this->setRouteToRequestMethod($route, $requestMethod);
232
        }
233
    }
234
235
    /**
236
     * Set the route to the request method.
237
     *
238
     * @param Route         $route         The route
239
     * @param RequestMethod $requestMethod The request method
240
     *
241
     * @return void
242
     */
243
    protected function setRouteToRequestMethod(Route $route, RequestMethod $requestMethod): void
244
    {
245
        $name  = $route->getName();
246
        $regex = $route->getRegex();
247
248
        // If this is a dynamic route
249
        if ($regex !== null) {
250
            // Set the route in the dynamic routes list
251
            $this->dynamic[$requestMethod->value][$regex] = $name;
252
        } // Otherwise set it in the static routes array
253
        else {
254
            // Set the route in the static routes list
255
            $this->static[$requestMethod->value][$route->getPath()] = $name;
256
        }
257
    }
258
259
    /**
260
     * Get a route of type (static|dynamic).
261
     *
262
     * @param RequestMethodList  $type   The type [static|dynamic]
263
     * @param string             $path   The path
264
     * @param RequestMethod|null $method [optional] The request method
265
     *
266
     * @throws JsonException
267
     *
268
     * @return Route|null
269
     */
270
    protected function getOfType(array $type, string $path, RequestMethod|null $method = null): Route|null
271
    {
272
        if ($method === null) {
273
            return $this->getAnyOfType($type, $path);
274
        }
275
276
        $route = $type[$method->value][$path] ?? null;
277
278
        if ($route === null) {
279
            return null;
280
        }
281
282
        return $this->ensureRoute($route);
283
    }
284
285
    /**
286
     * Get a route of any type (static|dynamic).
287
     *
288
     * @param RequestMethodList $type The type [static|dynamic]
289
     * @param string            $path The path
290
     *
291
     * @throws JsonException
292
     *
293
     * @return Route|null
294
     */
295
    protected function getAnyOfType(array $type, string $path): Route|null
296
    {
297
        return $this->getOfType($type, $path, RequestMethod::GET)
298
            ?? $this->getOfType($type, $path, RequestMethod::HEAD)
299
            ?? $this->getOfType($type, $path, RequestMethod::POST)
300
            ?? $this->getOfType($type, $path, RequestMethod::PUT)
301
            ?? $this->getOfType($type, $path, RequestMethod::PATCH)
302
            ?? $this->getOfType($type, $path, RequestMethod::DELETE);
303
    }
304
305
    /**
306
     * Has a path of type (static|dynamic).
307
     *
308
     * @param RequestMethodList  $type   The type [static|dynamic]
309
     * @param string             $path   The path
310
     * @param RequestMethod|null $method [optional] The request method
311
     *
312
     * @return bool
313
     */
314
    protected function hasOfType(array $type, string $path, RequestMethod|null $method = null): bool
315
    {
316
        if ($method === null) {
317
            return $this->hasAnyOfType($type, $path);
318
        }
319
320
        return isset($type[$method->value][$path]);
321
    }
322
323
    /**
324
     * Has a path of any type.
325
     *
326
     * @param RequestMethodList $type The type [static|dynamic]
327
     * @param string            $path The path
328
     *
329
     * @return bool
330
     */
331
    protected function hasAnyOfType(array $type, string $path): bool
332
    {
333
        foreach ($type as $requestMethod) {
334
            if (isset($requestMethod[$path])) {
335
                return true;
336
            }
337
        }
338
339
        return false;
340
    }
341
342
    /**
343
     * Get all of type with optional by request method.
344
     *
345
     * @param RequestMethodList  $type   The type [static|dynamic]
346
     * @param RequestMethod|null $method [optional] The request method
347
     *
348
     * @return RequestMethodRouteList|array<string, Route>
0 ignored issues
show
Bug introduced by
The type Valkyrja\Http\Routing\Co...\RequestMethodRouteList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
349
     */
350
    protected function allOfType(array $type, RequestMethod|null $method = null): array
351
    {
352
        if ($method === null) {
353
            return $this->ensureMethodRoutes($type);
354
        }
355
356
        return $this->ensureRoutes($type[$method->value] ?? []);
357
    }
358
359
    /**
360
     * Ensure request methods are arrays of routes.
361
     *
362
     * @param RequestMethodList $methodsArray
363
     *
364
     * @return RequestMethodRouteList
365
     */
366
    protected function ensureMethodRoutes(array $methodsArray): array
367
    {
368
        return array_map(
369
            [$this, 'ensureRoutes'],
370
            $methodsArray
371
        );
372
    }
373
374
    /**
375
     * Ensure an array is an array of routes.
376
     *
377
     * @param array<string, string|Route> $routesArray The routes array
378
     *
379
     * @return array<string, Route>
380
     */
381
    protected function ensureRoutes(array $routesArray): array
382
    {
383
        return array_map(
384
            [$this, 'ensureRoute'],
385
            $routesArray
386
        );
387
    }
388
389
    /**
390
     * Ensure a route, or null, is returned.
391
     *
392
     * @param Route|string $route The route
393
     *
394
     * @return Route
395
     */
396
    protected function ensureRoute(Route|string $route): Route
397
    {
398
        if (is_string($route) && isset($this->routes[$route])) {
399
            $route = $this->routes[$route];
400
        }
401
402
        if (is_string($route)) {
403
            $unserializedRoute = unserialize($route, ['allowed_classes' => true]);
404
405
            if (! $unserializedRoute instanceof Route) {
406
                throw new InvalidArgumentException('Invalid object serialized.');
407
            }
408
409
            return $unserializedRoute;
410
        }
411
412
        return $route;
413
    }
414
}
415