Passed
Pull Request — master (#196)
by Rustam
03:32
created

Route::methods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 1
eloc 9
c 3
b 1
f 0
nc 1
nop 8
dl 0
loc 19
ccs 10
cts 10
cp 1
crap 1
rs 9.9666

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use Attribute;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use Stringable;
11
use Yiisoft\Http\Method;
12
13
use function in_array;
14
15
/**
16
 * Route defines a mapping from URL to callback / name and vice versa.
17
 */
18
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
19
class Route implements Stringable
20
{
21
    private bool $actionAdded = false;
22
    /**
23
     * @var array[]|callable[]|string[]
24
     */
25
    private array $builtMiddlewares = [];
26
27
    /**
28
     * @param array $defaults Parameter default values indexed by parameter names.
29
     * @param bool $override Marks route as override. When added it will replace existing route with the same name.
30
     * @param array $disabledMiddlewares Excludes middleware from being invoked when action is handled.
31
     * It is useful to avoid invoking one of the parent group middleware for
32
     * a certain route.
33
     */
34 71
    public function __construct(
35
        private array $methods,
36
        private string $pattern,
37
        private ?string $name = null,
38
        private array $middlewares = [],
39
        private array $defaults = [],
40
        private array $hosts = [],
41
        private bool $override = false,
42
        private array $disabledMiddlewares = [],
43
    ) {
44 71
    }
45
46 60
    public static function get(
47
        string $pattern,
48
        ?string $name = null,
49
        array $middlewares = [],
50
        array $defaults = [],
51
        array $hosts = [],
52
        bool $override = false,
53
        array $disabledMiddlewares = []
54
    ): self {
55 60
        return self::methods(
56 60
            [Method::GET],
57 60
            $pattern,
58 60
            $name,
59 60
            $middlewares,
60 60
            $defaults,
61 60
            $hosts,
62 60
            $override,
63 60
            $disabledMiddlewares
64 60
        );
65
    }
66
67 8
    public static function post(
68
        string $pattern,
69
        ?string $name = null,
70
        array $middlewares = [],
71
        array $defaults = [],
72
        array $hosts = [],
73
        bool $override = false,
74
        array $disabledMiddlewares = []
75
    ): self {
76 8
        return self::methods(
77 8
            [Method::POST],
78 8
            $pattern,
79 8
            $name,
80 8
            $middlewares,
81 8
            $defaults,
82 8
            $hosts,
83 8
            $override,
84 8
            $disabledMiddlewares
85 8
        );
86
    }
87
88 4
    public static function put(
89
        string $pattern,
90
        ?string $name = null,
91
        array $middlewares = [],
92
        array $defaults = [],
93
        array $hosts = [],
94
        bool $override = false,
95
        array $disabledMiddlewares = []
96
    ): self {
97 4
        return self::methods(
98 4
            [Method::PUT],
99 4
            $pattern,
100 4
            $name,
101 4
            $middlewares,
102 4
            $defaults,
103 4
            $hosts,
104 4
            $override,
105 4
            $disabledMiddlewares
106 4
        );
107
    }
108
109 1
    public static function delete(
110
        string $pattern,
111
        ?string $name = null,
112
        array $middlewares = [],
113
        array $defaults = [],
114
        array $hosts = [],
115
        bool $override = false,
116
        array $disabledMiddlewares = []
117
    ): self {
118 1
        return self::methods(
119 1
            [Method::DELETE],
120 1
            $pattern,
121 1
            $name,
122 1
            $middlewares,
123 1
            $defaults,
124 1
            $hosts,
125 1
            $override,
126 1
            $disabledMiddlewares
127 1
        );
128
    }
129
130 1
    public static function patch(
131
        string $pattern,
132
        ?string $name = null,
133
        array $middlewares = [],
134
        array $defaults = [],
135
        array $hosts = [],
136
        bool $override = false,
137
        array $disabledMiddlewares = []
138
    ): self {
139 1
        return self::methods(
140 1
            [Method::PATCH],
141 1
            $pattern,
142 1
            $name,
143 1
            $middlewares,
144 1
            $defaults,
145 1
            $hosts,
146 1
            $override,
147 1
            $disabledMiddlewares
148 1
        );
149
    }
150
151 1
    public static function head(
152
        string $pattern,
153
        ?string $name = null,
154
        array $middlewares = [],
155
        array $defaults = [],
156
        array $hosts = [],
157
        bool $override = false,
158
        array $disabledMiddlewares = []
159
    ): self {
160 1
        return self::methods(
161 1
            [Method::HEAD],
162 1
            $pattern,
163 1
            $name,
164 1
            $middlewares,
165 1
            $defaults,
166 1
            $hosts,
167 1
            $override,
168 1
            $disabledMiddlewares
169 1
        );
170
    }
171
172 7
    public static function options(
173
        string $pattern,
174
        ?string $name = null,
175
        array $middlewares = [],
176
        array $defaults = [],
177
        array $hosts = [],
178
        bool $override = false,
179
        array $disabledMiddlewares = []
180
    ): self {
181 7
        return self::methods(
182 7
            [Method::OPTIONS],
183 7
            $pattern,
184 7
            $name,
185 7
            $middlewares,
186 7
            $defaults,
187 7
            $hosts,
188 7
            $override,
189 7
            $disabledMiddlewares
190 7
        );
191
    }
192
193
    /**
194
     * @param string[] $methods
195
     */
196 71
    public static function methods(
197
        array $methods,
198
        string $pattern,
199
        ?string $name = null,
200
        array $middlewares = [],
201
        array $defaults = [],
202
        array $hosts = [],
203
        bool $override = false,
204
        array $disabledMiddlewares = []
205
    ): self {
206 71
        return new self(
207 71
            methods: $methods,
208 71
            pattern: $pattern,
209 71
            name: $name,
210 71
            middlewares: $middlewares,
211 71
            defaults: $defaults,
212 71
            hosts: $hosts,
213 71
            override: $override,
214 71
            disabledMiddlewares: $disabledMiddlewares
215 71
        );
216
    }
217
218 23
    public function name(string $name): self
219
    {
220 23
        $route = clone $this;
221 23
        $route->name = $name;
222 23
        return $route;
223
    }
224
225 21
    public function pattern(string $pattern): self
226
    {
227 21
        $new = clone $this;
228 21
        $new->pattern = $pattern;
229 21
        return $new;
230
    }
231
232 8
    public function host(string $host): self
233
    {
234 8
        return $this->hosts($host);
235
    }
236
237 12
    public function hosts(string ...$hosts): self
238
    {
239 12
        $route = clone $this;
240 12
        $route->hosts = [];
241
242 12
        foreach ($hosts as $host) {
243 12
            $host = rtrim($host, '/');
244
245 12
            if ($host !== '' && !in_array($host, $route->hosts, true)) {
246 11
                $route->hosts[] = $host;
247
            }
248
        }
249
250 12
        return $route;
251
    }
252
253
    /**
254
     * Marks route as override. When added it will replace existing route with the same name.
255
     */
256 4
    public function override(): self
257
    {
258 4
        $route = clone $this;
259 4
        $route->override = true;
260 4
        return $route;
261
    }
262
263
    /**
264
     * Parameter default values indexed by parameter names.
265
     *
266
     * @psalm-param array<string,null|Stringable|scalar> $defaults
267
     */
268 3
    public function defaults(array $defaults): self
269
    {
270 3
        $route = clone $this;
271 3
        $route->defaults = array_map('\strval', $defaults);
272 3
        return $route;
273
    }
274
275
    /**
276
     * Appends a handler middleware definition that should be invoked for a matched route.
277
     * First added handler will be executed first.
278
     */
279 17
    public function middleware(array|callable|string ...$middlewareDefinition): self
280
    {
281 17
        if ($this->actionAdded) {
282 1
            throw new RuntimeException('middleware() can not be used after action().');
283
        }
284 16
        $route = clone $this;
285 16
        array_push(
286 16
            $route->middlewares,
287 16
            ...array_values($middlewareDefinition)
288 16
        );
289 16
        $route->builtMiddlewares = [];
290 16
        return $route;
291
    }
292
293
    /**
294
     * Prepends a handler middleware definition that should be invoked for a matched route.
295
     * Last added handler will be executed first.
296
     */
297 18
    public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
298
    {
299 18
        if (!$this->actionAdded) {
300 1
            throw new RuntimeException('prependMiddleware() can not be used before action().');
301
        }
302 17
        $route = clone $this;
303 17
        array_unshift(
304 17
            $route->middlewares,
305 17
            ...array_values($middlewareDefinition)
306 17
        );
307 17
        $route->builtMiddlewares = [];
308 17
        return $route;
309
    }
310
311
    /**
312
     * Appends action handler. It is a primary middleware definition that should be invoked last for a matched route.
313
     */
314 20
    public function action(array|callable|string $middlewareDefinition): self
315
    {
316 20
        $route = clone $this;
317 20
        $route->middlewares[] = $middlewareDefinition;
318 20
        $route->actionAdded = true;
319 20
        $route->builtMiddlewares = [];
320 20
        return $route;
321
    }
322
323
    /**
324
     * Excludes middleware from being invoked when action is handled.
325
     * It is useful to avoid invoking one of the parent group middleware for
326
     * a certain route.
327
     */
328 3
    public function disableMiddleware(mixed ...$middlewareDefinition): self
329
    {
330 3
        $route = clone $this;
331 3
        array_push(
332 3
            $route->disabledMiddlewares,
333 3
            ...array_values($middlewareDefinition)
334 3
        );
335 3
        $route->builtMiddlewares = [];
336 3
        return $route;
337
    }
338
339
    /**
340
     * @psalm-template T as string
341
     *
342
     * @psalm-param T $key
343
     *
344
     * @psalm-return (
345
     *   T is ('name'|'pattern') ? string :
346
     *       (T is 'host' ? string|null :
347
     *           (T is 'hosts' ? array<array-key, string> :
348
     *               (T is 'methods' ? array<array-key,string> :
349
     *                   (T is 'defaults' ? array<string,string> :
350
     *                       (T is ('override'|'hasMiddlewares') ? bool : mixed)
351
     *                   )
352
     *               )
353
     *           )
354
     *       )
355
     *    )
356
     */
357 45
    public function getData(string $key): mixed
358
    {
359 45
        return match ($key) {
360 45
            'name' => $this->name ??
361 26
                (implode(', ', $this->methods) . ' ' . implode('|', $this->hosts) . $this->pattern),
362 45
            'pattern' => $this->pattern,
363 45
            'host' => $this->hosts[0] ?? null,
364 45
            'hosts' => $this->hosts,
365 45
            'methods' => $this->methods,
366 45
            'defaults' => $this->defaults,
367 45
            'override' => $this->override,
368 45
            'hasMiddlewares' => $this->middlewares !== [],
369 45
            default => throw new InvalidArgumentException('Unknown data key: ' . $key),
370 45
        };
371
    }
372
373 4
    public function __toString(): string
374
    {
375 4
        $result = $this->name === null
376 1
            ? ''
377 3
            : '[' . $this->name . '] ';
378
379 4
        if ($this->methods !== []) {
380 4
            $result .= implode(',', $this->methods) . ' ';
381
        }
382
383 4
        if ($this->hosts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->hosts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
384 2
            $quoted = array_map(static fn ($host) => preg_quote($host, '/'), $this->hosts);
385
386 2
            if (!preg_match('/' . implode('|', $quoted) . '/', $this->pattern)) {
387 2
                $result .= implode('|', $this->hosts);
388
            }
389
        }
390
391 4
        $result .= $this->pattern;
392
393 4
        return $result;
394
    }
395
396 1
    public function __debugInfo()
397
    {
398 1
        return [
399 1
            'name' => $this->name,
400 1
            'methods' => $this->methods,
401 1
            'pattern' => $this->pattern,
402 1
            'hosts' => $this->hosts,
403 1
            'defaults' => $this->defaults,
404 1
            'override' => $this->override,
405 1
            'actionAdded' => $this->actionAdded,
406 1
            'middlewares' => $this->middlewares,
407 1
            'builtMiddlewares' => $this->builtMiddlewares,
408 1
            'disabledMiddlewares' => $this->disabledMiddlewares,
409 1
        ];
410
    }
411
412
    /**
413
     * @return array[]|callable[]|string[]
414
     */
415 15
    public function getBuiltMiddlewares(): array
416
    {
417
        // Don't build middlewares if we did it earlier.
418
        // This improves performance in event-loop applications.
419 15
        if ($this->builtMiddlewares !== []) {
420
            return $this->builtMiddlewares;
421
        }
422
423 15
        $builtMiddlewares = $this->middlewares;
424
425 15
        foreach ($builtMiddlewares as $index => $definition) {
426 14
            if (in_array($definition, $this->disabledMiddlewares, true)) {
427 1
                unset($builtMiddlewares[$index]);
428
            }
429
        }
430
431 15
        return $this->builtMiddlewares = $builtMiddlewares;
432
    }
433
}
434