Collection::allDynamic()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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