Passed
Pull Request — master (#38)
by Melech
05:50 queued 01:43
created

Collection::getData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 14
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
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 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...
18
use Valkyrja\Http\Routing\Collection\Contract\Collection as Contract;
19
use Valkyrja\Http\Routing\Data;
20
use Valkyrja\Http\Routing\Data\Contract\Route;
21
use Valkyrja\Http\Routing\Exception\InvalidArgumentException;
22
23
use function array_map;
24
use function array_merge;
25
use function assert;
26
use function is_string;
27
28
/**
29
 * Class Collection.
30
 *
31
 * @author Melech Mizrachi
32
 *
33
 * @phpstan-import-type RequestMethodList from Contract
34
 * @phpstan-import-type RequestMethodRouteList from Contract
35
 *
36
 * @psalm-import-type RequestMethodList from Contract
37
 * @psalm-import-type RequestMethodRouteList from Contract
38
 */
39
class Collection implements Contract
40
{
41
    /**
42
     * The routes.
43
     *
44
     * @var array<string, Route|string>
45
     */
46
    protected array $routes = [];
47
48
    /**
49
     * The static routes.
50
     *
51
     * @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...
52
     */
53
    protected array $static = [];
54
55
    /**
56
     * The dynamic routes.
57
     *
58
     * @var RequestMethodList
59
     */
60
    protected array $dynamic = [];
61
62
    /**
63
     * The named routes.
64
     *
65
     * @var array<string, string>
66
     */
67
    protected array $named = [];
68
69
    /**
70
     * @inheritDoc
71
     */
72
    public function getData(): Data
73
    {
74
        $data = new Data();
75
76
        $data->routes  = [];
77
        $data->static  = $this->static;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->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...
78
        $data->dynamic = $this->dynamic;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->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...
79
        $data->named   = $this->named;
80
81
        foreach ($this->routes as $id => $route) {
82
            $data->routes[$id] = serialize($route);
83
        }
84
85
        return $data;
86
    }
87
88
    /**
89
     * @inheritDoc
90
     */
91
    public function setFromData(Data $data): void
92
    {
93
        $this->routes  = $data->routes;
94
        $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...
95
        $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\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...
96
        $this->named   = $data->named;
97
    }
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function add(Route $route): void
103
    {
104
        assert($route->getPath());
105
106
        // Set the route to its request methods
107
        $this->setRouteToRequestMethods($route);
108
        // Set the route to the named
109
        $this->setRouteToNamed($route);
110
111
        $this->routes[$route->getName()] = $route;
112
    }
113
114
    /**
115
     * @inheritDoc
116
     *
117
     * @throws JsonException
118
     */
119
    public function get(string $path, RequestMethod|null $method = null): Route|null
120
    {
121
        return $this->getStatic($path, $method) ?? $this->getDynamic($path, $method);
122
    }
123
124
    /**
125
     * @inheritDoc
126
     */
127
    public function isset(string $path, RequestMethod|null $method = null): bool
128
    {
129
        return $this->hasStatic($path, $method) || $this->hasDynamic($path, $method);
130
    }
131
132
    /**
133
     * @inheritDoc
134
     */
135
    public function all(): array
136
    {
137
        return $this->ensureMethodRoutes(array_merge($this->static, $this->dynamic));
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function allFlattened(): array
144
    {
145
        return $this->ensureRoutes($this->routes);
146
    }
147
148
    /**
149
     * @inheritDoc
150
     *
151
     * @throws JsonException
152
     */
153
    public function getStatic(string $path, RequestMethod|null $method = null): Route|null
154
    {
155
        return $this->getOfType($this->static, $path, $method);
156
    }
157
158
    /**
159
     * @inheritDoc
160
     */
161
    public function hasStatic(string $path, RequestMethod|null $method = null): bool
162
    {
163
        return $this->hasOfType($this->static, $path, $method);
164
    }
165
166
    /**
167
     * @inheritDoc
168
     */
169
    public function allStatic(RequestMethod|null $method = null): array
170
    {
171
        return $this->allOfType($this->static, $method);
172
    }
173
174
    /**
175
     * @inheritDoc
176
     *
177
     * @throws JsonException
178
     */
179
    public function getDynamic(string $regex, RequestMethod|null $method = null): Route|null
180
    {
181
        return $this->getOfType($this->dynamic, $regex, $method);
182
    }
183
184
    /**
185
     * @inheritDoc
186
     */
187
    public function hasDynamic(string $regex, RequestMethod|null $method = null): bool
188
    {
189
        return $this->hasOfType($this->dynamic, $regex, $method);
190
    }
191
192
    /**
193
     * @inheritDoc
194
     */
195
    public function allDynamic(RequestMethod|null $method = null): array
196
    {
197
        return $this->allOfType($this->dynamic, $method);
198
    }
199
200
    /**
201
     * @inheritDoc
202
     */
203
    public function getNamed(string $name): Route|null
204
    {
205
        $named = $this->named[$name] ?? null;
206
207
        if ($named === null) {
208
            return null;
209
        }
210
211
        return $this->ensureRoute($named);
212
    }
213
214
    /**
215
     * @inheritDoc
216
     */
217
    public function hasNamed(string $name): bool
218
    {
219
        return isset($this->named[$name]);
220
    }
221
222
    /**
223
     * @inheritDoc
224
     */
225
    public function allNamed(): array
226
    {
227
        return $this->ensureRoutes($this->named);
228
    }
229
230
    /**
231
     * Set a route to its request methods.
232
     *
233
     * @param Route $route The route
234
     *
235
     * @return void
236
     */
237
    protected function setRouteToRequestMethods(Route $route): void
238
    {
239
        foreach ($route->getRequestMethods() as $requestMethod) {
240
            $this->setRouteToRequestMethod($route, $requestMethod);
241
        }
242
    }
243
244
    /**
245
     * Set the route to the request method.
246
     *
247
     * @param Route         $route         The route
248
     * @param RequestMethod $requestMethod The request method
249
     *
250
     * @return void
251
     */
252
    protected function setRouteToRequestMethod(Route $route, RequestMethod $requestMethod): void
253
    {
254
        $name  = $route->getName();
255
        $regex = $route->getRegex();
256
257
        // If this is a dynamic route
258
        if ($regex !== null) {
259
            // Set the route in the dynamic routes list
260
            $this->dynamic[$requestMethod->value][$regex] = $name;
261
        } // Otherwise set it in the static routes array
262
        else {
263
            // Set the route in the static routes list
264
            $this->static[$requestMethod->value][$route->getPath()] = $name;
265
        }
266
    }
267
268
    /**
269
     * Set the route to the named.
270
     *
271
     * @param Route $route The route
272
     *
273
     * @return void
274
     */
275
    protected function setRouteToNamed(Route $route): void
276
    {
277
        $name = $route->getName();
278
279
        // Set the route in the named routes list
280
        $this->named[$name] = $name;
281
    }
282
283
    /**
284
     * Get a route of type (static|dynamic).
285
     *
286
     * @param RequestMethodList  $type   The type [static|dynamic]
287
     * @param string             $path   The path
288
     * @param RequestMethod|null $method [optional] The request method
289
     *
290
     * @throws JsonException
291
     *
292
     * @return Route|null
293
     */
294
    protected function getOfType(array $type, string $path, RequestMethod|null $method = null): Route|null
295
    {
296
        if ($method === null) {
297
            return $this->getAnyOfType($type, $path);
298
        }
299
300
        $route = $type[$method->value][$path] ?? null;
301
302
        if ($route === null) {
303
            return null;
304
        }
305
306
        return $this->ensureRoute($route);
307
    }
308
309
    /**
310
     * Get a route of any type (static|dynamic).
311
     *
312
     * @param RequestMethodList $type The type [static|dynamic]
313
     * @param string            $path The path
314
     *
315
     * @throws JsonException
316
     *
317
     * @return Route|null
318
     */
319
    protected function getAnyOfType(array $type, string $path): Route|null
320
    {
321
        return $this->getOfType($type, $path, RequestMethod::GET)
322
            ?? $this->getOfType($type, $path, RequestMethod::HEAD)
323
            ?? $this->getOfType($type, $path, RequestMethod::POST)
324
            ?? $this->getOfType($type, $path, RequestMethod::PUT)
325
            ?? $this->getOfType($type, $path, RequestMethod::PATCH)
326
            ?? $this->getOfType($type, $path, RequestMethod::DELETE);
327
    }
328
329
    /**
330
     * Has a path of type (static|dynamic).
331
     *
332
     * @param RequestMethodList  $type   The type [static|dynamic]
333
     * @param string             $path   The path
334
     * @param RequestMethod|null $method [optional] The request method
335
     *
336
     * @return bool
337
     */
338
    protected function hasOfType(array $type, string $path, RequestMethod|null $method = null): bool
339
    {
340
        if ($method === null) {
341
            return $this->hasAnyOfType($type, $path);
342
        }
343
344
        return isset($type[$method->value][$path]);
345
    }
346
347
    /**
348
     * Has a path of any type.
349
     *
350
     * @param RequestMethodList $type The type [static|dynamic]
351
     * @param string            $path The path
352
     *
353
     * @return bool
354
     */
355
    protected function hasAnyOfType(array $type, string $path): bool
356
    {
357
        foreach ($type as $requestMethod) {
358
            if (isset($requestMethod[$path])) {
359
                return true;
360
            }
361
        }
362
363
        return false;
364
    }
365
366
    /**
367
     * Get all of type with optional by request method.
368
     *
369
     * @param RequestMethodList  $type   The type [static|dynamic]
370
     * @param RequestMethod|null $method [optional] The request method
371
     *
372
     * @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...
373
     */
374
    protected function allOfType(array $type, RequestMethod|null $method = null): array
375
    {
376
        if ($method === null) {
377
            return $this->ensureMethodRoutes($type);
378
        }
379
380
        return $this->ensureRoutes($type[$method->value] ?? []);
381
    }
382
383
    /**
384
     * Ensure request methods are arrays of routes.
385
     *
386
     * @param RequestMethodList $methodsArray
387
     *
388
     * @return RequestMethodRouteList
389
     */
390
    protected function ensureMethodRoutes(array $methodsArray): array
391
    {
392
        return array_map(
393
            [$this, 'ensureRoutes'],
394
            $methodsArray
395
        );
396
    }
397
398
    /**
399
     * Ensure an array is an array of routes.
400
     *
401
     * @param array<string, string|Route> $routesArray The routes array
402
     *
403
     * @return array<string, Route>
404
     */
405
    protected function ensureRoutes(array $routesArray): array
406
    {
407
        return array_map(
408
            [$this, 'ensureRoute'],
409
            $routesArray
410
        );
411
    }
412
413
    /**
414
     * Ensure a route, or null, is returned.
415
     *
416
     * @param Route|string $route The route
417
     *
418
     * @return Route
419
     */
420
    protected function ensureRoute(Route|string $route): Route
421
    {
422
        if (is_string($route) && isset($this->routes[$route])) {
423
            $route = $this->routes[$route];
424
        }
425
426
        if (is_string($route)) {
427
            $unserializedRoute = unserialize($route, ['allowed_classes' => true]);
428
429
            if (! $unserializedRoute instanceof Route) {
430
                throw new InvalidArgumentException('Invalid object serialized.');
431
            }
432
433
            return $unserializedRoute;
434
        }
435
436
        return $route;
437
    }
438
}
439