Passed
Pull Request — master (#196)
by Rustam
12:45
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 77
    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 77
    }
45
46 65
    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 65
        return self::methods(
56 65
            [Method::GET],
57 65
            $pattern,
58 65
            $name,
59 65
            $middlewares,
60 65
            $defaults,
61 65
            $hosts,
62 65
            $override,
63 65
            $disabledMiddlewares
64 65
        );
65
    }
66
67 9
    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 9
        return self::methods(
77 9
            [Method::POST],
78 9
            $pattern,
79 9
            $name,
80 9
            $middlewares,
81 9
            $defaults,
82 9
            $hosts,
83 9
            $override,
84 9
            $disabledMiddlewares
85 9
        );
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 9
    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 9
        return self::methods(
182 9
            [Method::OPTIONS],
183 9
            $pattern,
184 9
            $name,
185 9
            $middlewares,
186 9
            $defaults,
187 9
            $hosts,
188 9
            $override,
189 9
            $disabledMiddlewares
190 9
        );
191
    }
192
193
    /**
194
     * @param string[] $methods
195
     */
196 77
    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 77
        return new self(
207 77
            methods: $methods,
208 77
            pattern: $pattern,
209 77
            name: $name,
210 77
            middlewares: $middlewares,
211 77
            defaults: $defaults,
212 77
            hosts: $hosts,
213 77
            override: $override,
214 77
            disabledMiddlewares: $disabledMiddlewares
215 77
        );
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 23
    public function pattern(string $pattern): self
226
    {
227 23
        $new = clone $this;
228 23
        $new->pattern = $pattern;
229 23
        return $new;
230
    }
231
232 9
    public function host(string $host): self
233
    {
234 9
        return $this->hosts($host);
235
    }
236
237 13
    public function hosts(string ...$hosts): self
238
    {
239 13
        $route = clone $this;
240 13
        $route->hosts = [];
241
242 13
        foreach ($hosts as $host) {
243 13
            $host = rtrim($host, '/');
244
245 13
            if ($host !== '' && !in_array($host, $route->hosts, true)) {
246 12
                $route->hosts[] = $host;
247
            }
248
        }
249
250 13
        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 23
    public function middleware(array|callable|string ...$middlewareDefinition): self
280
    {
281 23
        if ($this->actionAdded) {
282 1
            throw new RuntimeException('middleware() can not be used after action().');
283
        }
284 22
        $route = clone $this;
285 22
        array_push(
286 22
            $route->middlewares,
287 22
            ...array_values($middlewareDefinition)
288 22
        );
289 22
        $route->builtMiddlewares = [];
290 22
        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 23
    public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
298
    {
299 23
        if (!$this->actionAdded) {
300 1
            throw new RuntimeException('prependMiddleware() can not be used before action().');
301
        }
302 22
        $route = clone $this;
303 22
        array_unshift(
304 22
            $route->middlewares,
305 22
            ...array_values($middlewareDefinition)
306 22
        );
307 22
        $route->builtMiddlewares = [];
308 22
        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 26
    public function action(array|callable|string $middlewareDefinition): self
315
    {
316 26
        $route = clone $this;
317 26
        $route->middlewares[] = $middlewareDefinition;
318 26
        $route->actionAdded = true;
319 26
        $route->builtMiddlewares = [];
320 26
        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 50
    public function getData(string $key): mixed
358
    {
359 50
        return match ($key) {
360 50
            'name' => $this->name ??
361 31
                (implode(', ', $this->methods) . ' ' . implode('|', $this->hosts) . $this->pattern),
362 50
            'pattern' => $this->pattern,
363 50
            'host' => $this->hosts[0] ?? null,
364 50
            'hosts' => $this->hosts,
365 50
            'methods' => $this->methods,
366 50
            'defaults' => $this->defaults,
367 50
            'override' => $this->override,
368 50
            'hasMiddlewares' => $this->middlewares !== [],
369 50
            default => throw new InvalidArgumentException('Unknown data key: ' . $key),
370 50
        };
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 20
    public function getBuiltMiddlewares(): array
416
    {
417
        // Don't build middlewares if we did it earlier.
418
        // This improves performance in event-loop applications.
419 20
        if ($this->builtMiddlewares !== []) {
420 1
            return $this->builtMiddlewares;
421
        }
422
423 20
        $builtMiddlewares = $this->middlewares;
424
425 20
        foreach ($builtMiddlewares as $index => $definition) {
426 19
            if (in_array($definition, $this->disabledMiddlewares, true)) {
427 1
                unset($builtMiddlewares[$index]);
428
            }
429
        }
430
431 20
        return $this->builtMiddlewares = $builtMiddlewares;
432
    }
433
}
434