Passed
Push — master ( 91f02d...65071d )
by Melech
21:33 queued 07:21
created

Collection::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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