Passed
Pull Request — master (#196)
by Dmitriy
02:48
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 75
    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 75
    }
45
46 64
    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 64
        return self::methods(
56 64
            [Method::GET],
57 64
            $pattern,
58 64
            $name,
59 64
            $middlewares,
60 64
            $defaults,
61 64
            $hosts,
62 64
            $override,
63 64
            $disabledMiddlewares
64 64
        );
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 75
    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 75
        return new self(
207 75
            methods: $methods,
208 75
            pattern: $pattern,
209 75
            name: $name,
210 75
            middlewares: $middlewares,
211 75
            defaults: $defaults,
212 75
            hosts: $hosts,
213 75
            override: $override,
214 75
            disabledMiddlewares: $disabledMiddlewares
215 75
        );
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 21
    public function middleware(array|callable|string ...$middlewareDefinition): self
280
    {
281 21
        if ($this->actionAdded) {
282 1
            throw new RuntimeException('middleware() can not be used after action().');
283
        }
284 20
        $route = clone $this;
285 20
        array_push(
286 20
            $route->middlewares,
287 20
            ...array_values($middlewareDefinition)
288 20
        );
289 20
        $route->builtMiddlewares = [];
290 20
        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 21
    public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
298
    {
299 21
        if (!$this->actionAdded) {
300 1
            throw new RuntimeException('prependMiddleware() can not be used before action().');
301
        }
302 20
        $route = clone $this;
303 20
        array_unshift(
304 20
            $route->middlewares,
305 20
            ...array_values($middlewareDefinition)
306 20
        );
307 20
        $route->builtMiddlewares = [];
308 20
        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 24
    public function action(array|callable|string $middlewareDefinition): self
315
    {
316 24
        $route = clone $this;
317 24
        $route->middlewares[] = $middlewareDefinition;
318 24
        $route->actionAdded = true;
319 24
        $route->builtMiddlewares = [];
320 24
        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 48
    public function getData(string $key): mixed
358
    {
359 48
        return match ($key) {
360 48
            'name' => $this->name ??
361 29
                (implode(', ', $this->methods) . ' ' . implode('|', $this->hosts) . $this->pattern),
362 48
            'pattern' => $this->pattern,
363 48
            'host' => $this->hosts[0] ?? null,
364 48
            'hosts' => $this->hosts,
365 48
            'methods' => $this->methods,
366 48
            'defaults' => $this->defaults,
367 48
            'override' => $this->override,
368 48
            'hasMiddlewares' => $this->middlewares !== [],
369 48
            default => throw new InvalidArgumentException('Unknown data key: ' . $key),
370 48
        };
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 19
    public function getBuiltMiddlewares(): array
416
    {
417
        // Don't build middlewares if we did it earlier.
418
        // This improves performance in event-loop applications.
419 19
        if ($this->builtMiddlewares !== []) {
420 1
            return $this->builtMiddlewares;
421
        }
422
423 19
        $builtMiddlewares = $this->middlewares;
424
425 19
        foreach ($builtMiddlewares as $index => $definition) {
426 18
            if (in_array($definition, $this->disabledMiddlewares, true)) {
427 1
                unset($builtMiddlewares[$index]);
428
            }
429
        }
430
431 19
        return $this->builtMiddlewares = $builtMiddlewares;
432
    }
433
}
434