Passed
Branch master (e894a3)
by Melech
15:39 queued 01:19
created

Route::__construct()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 50
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 24
dl 0
loc 50
rs 8.9137
c 1
b 0
f 0
cc 6
nc 16
nop 14

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
/*
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\Model;
15
16
use Valkyrja\Dispatcher\Model\Dispatch;
17
use Valkyrja\Http\Message\Enum\RequestMethod;
18
use Valkyrja\Http\Message\Enum\StatusCode;
19
use Valkyrja\Http\Middleware\Contract\RouteDispatchedMiddleware;
20
use Valkyrja\Http\Middleware\Contract\RouteMatchedMiddleware;
21
use Valkyrja\Http\Middleware\Contract\SendingResponseMiddleware;
22
use Valkyrja\Http\Middleware\Contract\TerminatedMiddleware;
23
use Valkyrja\Http\Middleware\Contract\ThrowableCaughtMiddleware;
24
use Valkyrja\Http\Routing\Constant\Regex;
25
use Valkyrja\Http\Routing\Exception\InvalidRoutePathException;
26
use Valkyrja\Http\Routing\Middleware\RedirectRouteMiddleware;
27
use Valkyrja\Http\Routing\Middleware\SecureRouteMiddleware;
28
use Valkyrja\Http\Routing\Model\Contract\Route as Contract;
29
use Valkyrja\Http\Routing\Model\Parameter\Parameter;
30
use Valkyrja\Http\Struct\Request\Contract\RequestStruct;
31
use Valkyrja\Http\Struct\Response\Contract\ResponseStruct;
32
use Valkyrja\Type\BuiltIn\Support\Cls;
33
use Valkyrja\Type\Data\Cast;
34
35
use function array_map;
36
use function array_merge;
37
use function assert;
38
use function is_a;
39
use function is_array;
40
use function is_int;
41
use function is_string;
42
43
/**
44
 * Class Route.
45
 *
46
 * @author Melech Mizrachi
47
 */
48
class Route extends Dispatch implements Contract
49
{
50
    /** @var string */
51
    protected const string DEFAULT_PATH = '/';
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 51 at column 27
Loading history...
52
    /** @var string|null */
53
    protected const string|null DEFAULT_NAME = null;
54
    /** @var RequestMethod[] */
55
    protected const array DEFAULT_METHODS = [
56
        RequestMethod::GET,
57
        RequestMethod::HEAD,
58
    ];
59
    /** @var bool|null */
60
    protected const bool|null DEFAULT_SECURE = null;
61
    /** @var string|null */
62
    protected const string|null DEFAULT_TO = null;
63
    /** @var StatusCode|null */
64
    protected const StatusCode|null DEFAULT_CODE = null;
65
66
    /**
67
     * The path for this route.
68
     *
69
     * @var string
70
     */
71
    protected string $path = '/';
72
73
    /**
74
     * The redirect path for this route.
75
     *
76
     * @var string|null
77
     */
78
    protected string|null $to;
79
80
    /**
81
     * The redirect status code for this route.
82
     *
83
     * @var StatusCode|null
84
     */
85
    protected StatusCode|null $code;
86
87
    /**
88
     * The request methods for this route.
89
     *
90
     * @var RequestMethod[]
91
     */
92
    protected array $methods = [
93
        RequestMethod::GET,
94
        RequestMethod::HEAD,
95
    ];
96
97
    /**
98
     * The regex for dynamic routes.
99
     *
100
     * @var string|null
101
     */
102
    protected string|null $regex;
103
104
    /**
105
     * The dynamic parameters.
106
     *
107
     * @var array<array-key, Parameter>
108
     */
109
    protected array $parameters;
110
111
    /**
112
     * The middleware for this route.
113
     *
114
     * @var class-string<RouteMatchedMiddleware>[]|null
115
     */
116
    protected array|null $matchedMiddleware;
117
118
    /**
119
     * The middleware for this route.
120
     *
121
     * @var class-string<RouteDispatchedMiddleware>[]|null
122
     */
123
    protected array|null $dispatchedMiddleware;
124
125
    /**
126
     * The middleware for this route.
127
     *
128
     * @var class-string<ThrowableCaughtMiddleware>[]|null
129
     */
130
    protected array|null $exceptionMiddleware;
131
132
    /**
133
     * The middleware for this route.
134
     *
135
     * @var class-string<SendingResponseMiddleware>[]|null
136
     */
137
    protected array|null $sendingMiddleware;
138
139
    /**
140
     * The middleware for this route.
141
     *
142
     * @var class-string<TerminatedMiddleware>[]|null
143
     */
144
    protected array|null $terminatedMiddleware;
145
146
    /**
147
     * The request struct for this route.
148
     *
149
     * @var class-string<RequestStruct>|null
150
     */
151
    protected string|null $requestStruct;
152
153
    /**
154
     * The response struct for this route.
155
     *
156
     * @var class-string<ResponseStruct>|null
157
     */
158
    protected string|null $responseStruct;
159
160
    /**
161
     * Whether the route is dynamic.
162
     *
163
     * @var bool
164
     */
165
    protected bool $dynamic = false;
166
167
    /**
168
     * Whether the route is secure.
169
     *
170
     * @var bool
171
     */
172
    protected bool $secure = false;
173
174
    /**
175
     * Whether the route is a redirect.
176
     *
177
     * @var bool
178
     */
179
    protected bool $redirect = false;
180
181
    /**
182
     * @param non-empty-string|null                          $path                 [optional] The path
183
     * @param RequestMethod[]|null                           $methods              The request methods
184
     * @param Parameter[]|null                               $parameters           The parameters
185
     * @param class-string<RouteMatchedMiddleware>[]|null    $matchedMiddleware    The matched middleware
186
     * @param class-string<RouteDispatchedMiddleware>[]|null $dispatchedMiddleware The dispatched middleware
187
     * @param class-string<ThrowableCaughtMiddleware>[]|null $exceptionMiddleware  The exception middleware
188
     * @param class-string<SendingResponseMiddleware>[]|null $sendingMiddleware    The sending middleware
189
     * @param class-string<TerminatedMiddleware>[]|null      $terminatedMiddleware The terminated middleware
190
     * @param class-string<RequestStruct>|null               $requestStruct        The request struct
191
     * @param class-string<ResponseStruct>|null              $responseStruct       The response struct
192
     *
193
     * @throws InvalidRoutePathException
194
     */
195
    public function __construct(
196
        string|null $path = null,
197
        string|null $name = null,
198
        array|null $methods = null,
199
        array|null $parameters = null,
200
        array|null $matchedMiddleware = null,
201
        array|null $dispatchedMiddleware = null,
202
        array|null $exceptionMiddleware = null,
203
        array|null $sendingMiddleware = null,
204
        array|null $terminatedMiddleware = null,
205
        string|null $requestStruct = null,
206
        string|null $responseStruct = null,
207
        bool|null $secure = null,
208
        string|null $to = null,
209
        StatusCode|null $code = null,
210
    ) {
211
        $path    ??= static::DEFAULT_PATH;
212
        $name    ??= static::DEFAULT_NAME;
213
        $methods ??= static::DEFAULT_METHODS;
214
        $secure  ??= static::DEFAULT_SECURE;
215
        $to      ??= static::DEFAULT_TO;
216
        $code    ??= static::DEFAULT_CODE;
217
218
        $this->setPath($path);
219
        $this->setMethods($methods);
220
221
        if ($name !== null && $name !== '') {
222
            $this->name = $name;
223
        }
224
225
        if ($parameters !== null) {
226
            $this->setParameters($parameters);
227
        }
228
229
        if ($secure !== null) {
230
            $this->secure = $secure;
231
        }
232
233
        if ($to !== '') {
234
            $this->setTo($to);
235
        }
236
237
        $this->setMatchedMiddleware($matchedMiddleware);
238
        $this->setDispatchedMiddleware($dispatchedMiddleware);
239
        $this->setExceptionMiddleware($exceptionMiddleware);
240
        $this->setSendingMiddleware($sendingMiddleware);
241
        $this->setTerminatedMiddleware($terminatedMiddleware);
242
        $this->setCode($code);
243
        $this->setRequestStruct($requestStruct);
244
        $this->setResponseStruct($responseStruct);
245
    }
246
247
    /**
248
     * @inheritDoc
249
     */
250
    public function getPath(): string
251
    {
252
        return $this->path;
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function setPath(string $path): static
259
    {
260
        if ($path === '') {
261
            throw new InvalidRoutePathException('Path must be a non-empty-string.');
262
        }
263
264
        $this->path = $path;
265
266
        return $this;
267
    }
268
269
    /**
270
     * @inheritDoc
271
     */
272
    public function withPath(string $path): static
273
    {
274
        $route = clone $this;
275
276
        $route->path .= $path;
277
278
        return $route;
279
    }
280
281
    /**
282
     * @inheritDoc
283
     */
284
    public function withName(string $name): static
285
    {
286
        $route = clone $this;
287
288
        $currentName = $this->name ?? '';
289
290
        if ($name) {
291
            $route->name = $currentName
292
                ? "$currentName.$name"
293
                : $name;
294
        }
295
296
        return $route;
297
    }
298
299
    /**
300
     * @inheritDoc
301
     */
302
    public function getTo(): string|null
303
    {
304
        return $this->to ?? null;
305
    }
306
307
    /**
308
     * @inheritDoc
309
     */
310
    public function setTo(string|null $to = null): static
311
    {
312
        if ($to !== null) {
313
            $this->setRedirect(true);
314
        }
315
316
        $this->to = $to;
317
318
        return $this;
319
    }
320
321
    /**
322
     * @inheritDoc
323
     */
324
    public function getCode(): StatusCode|null
325
    {
326
        return $this->code ?? null;
327
    }
328
329
    /**
330
     * @inheritDoc
331
     */
332
    public function setCode(StatusCode|int|null $code = null): static
333
    {
334
        $this->code = is_int($code)
335
            ? StatusCode::from($code)
336
            : $code;
337
338
        return $this;
339
    }
340
341
    /**
342
     * @inheritDoc
343
     */
344
    public function getMethods(): array
345
    {
346
        return $this->methods;
347
    }
348
349
    /**
350
     * @inheritDoc
351
     */
352
    public function setMethods(array $methods): static
353
    {
354
        $this->methods = array_map(
355
            callback: static fn (RequestMethod|string $method): RequestMethod => is_string($method)
356
                ? RequestMethod::from($method)
357
                : $method,
358
            array: $methods
359
        );
360
361
        return $this;
362
    }
363
364
    /**
365
     * @inheritDoc
366
     */
367
    public function getRegex(): string|null
368
    {
369
        return $this->regex ?? null;
370
    }
371
372
    /**
373
     * @inheritDoc
374
     */
375
    public function setRegex(string|null $regex = null): static
376
    {
377
        $this->regex = $regex;
378
379
        return $this;
380
    }
381
382
    /**
383
     * @inheritDoc
384
     */
385
    public function getParameters(): array
386
    {
387
        return $this->parameters ?? [];
388
    }
389
390
    /**
391
     * @inheritDoc
392
     */
393
    public function setParameters(array $parameters): static
394
    {
395
        $this->parameters = array_map(
396
            static fn (Parameter|array $parameter): Parameter => is_array($parameter)
397
                ? Parameter::fromArray($parameter)
398
                : $parameter,
399
            $parameters
400
        );
401
402
        return $this;
403
    }
404
405
    /**
406
     * @inheritDoc
407
     */
408
    public function setParameter(Parameter $parameter): static
409
    {
410
        $this->parameters ??= [];
411
412
        $this->parameters[] = $parameter;
413
414
        return $this;
415
    }
416
417
    /**
418
     * @inheritDoc
419
     */
420
    public function addParameter(
421
        string $name,
422
        string|null $regex = null,
423
        Cast|null $cast = null,
424
        bool $isOptional = false,
425
        bool $shouldCapture = true,
426
        mixed $default = null
427
    ): static {
428
        return $this->setParameter(
429
            new Parameter(
430
                name: $name,
431
                regex: $regex ?? Regex::ANY,
432
                cast: $cast,
433
                isOptional: $isOptional,
434
                shouldCapture: $shouldCapture,
435
                default: $default
436
            )
437
        );
438
    }
439
440
    /**
441
     * @inheritDoc
442
     */
443
    public function getMiddleware(): array|null
444
    {
445
        return array_merge(
446
            $this->matchedMiddleware ?? [],
447
            $this->dispatchedMiddleware ?? [],
448
            $this->exceptionMiddleware ?? [],
449
            $this->sendingMiddleware ?? [],
450
            $this->terminatedMiddleware ?? [],
451
        );
452
    }
453
454
    /**
455
     * @inheritDoc
456
     */
457
    public function setMiddleware(array|null $middleware = null): static
458
    {
459
        $this->matchedMiddleware    = [];
460
        $this->dispatchedMiddleware = [];
461
        $this->exceptionMiddleware  = [];
462
        $this->sendingMiddleware    = [];
463
        $this->terminatedMiddleware = [];
464
465
        $this->addMiddlewares($middleware ?? []);
466
467
        return $this;
468
    }
469
470
    /**
471
     * @inheritDoc
472
     */
473
    public function withMiddleware(array $middleware): static
474
    {
475
        $route = clone $this;
476
477
        $route->addMiddlewares($middleware);
478
479
        return $route;
480
    }
481
482
    /**
483
     * @inheritDoc
484
     */
485
    public function addMiddleware(string $middleware): static
486
    {
487
        if (Cls::inherits($middleware, RouteMatchedMiddleware::class)) {
488
            /** @var class-string<RouteMatchedMiddleware> $middleware */
489
            $this->matchedMiddleware   ??= [];
490
            $this->matchedMiddleware[] = $middleware;
491
        }
492
493
        if (Cls::inherits($middleware, RouteDispatchedMiddleware::class)) {
494
            /** @var class-string<RouteDispatchedMiddleware> $middleware */
495
            $this->dispatchedMiddleware   ??= [];
496
            $this->dispatchedMiddleware[] = $middleware;
497
        }
498
499
        if (Cls::inherits($middleware, ThrowableCaughtMiddleware::class)) {
500
            /** @var class-string<ThrowableCaughtMiddleware> $middleware */
501
            $this->exceptionMiddleware   ??= [];
502
            $this->exceptionMiddleware[] = $middleware;
503
        }
504
505
        if (Cls::inherits($middleware, SendingResponseMiddleware::class)) {
506
            /** @var class-string<SendingResponseMiddleware> $middleware */
507
            $this->sendingMiddleware   ??= [];
508
            $this->sendingMiddleware[] = $middleware;
509
        }
510
511
        if (Cls::inherits($middleware, TerminatedMiddleware::class)) {
512
            /** @var class-string<TerminatedMiddleware> $middleware */
513
            $this->terminatedMiddleware   ??= [];
514
            $this->terminatedMiddleware[] = $middleware;
515
        }
516
517
        return $this;
518
    }
519
520
    /**
521
     * @inheritDoc
522
     */
523
    public function addMiddlewares(array $middleware): static
524
    {
525
        array_map(fn (string $middlewareItem) => $this->addMiddleware($middlewareItem), $middleware);
526
527
        return $this;
528
    }
529
530
    /**
531
     * @inheritDoc
532
     *
533
     * @return class-string<RouteMatchedMiddleware>[]|null
534
     */
535
    public function getMatchedMiddleware(): array|null
536
    {
537
        return $this->matchedMiddleware ?? null;
538
    }
539
540
    /**
541
     * @inheritDoc
542
     */
543
    public function setMatchedMiddleware(array|null $middleware = null): static
544
    {
545
        $this->matchedMiddleware = $middleware;
546
547
        return $this;
548
    }
549
550
    /**
551
     * @inheritDoc
552
     */
553
    public function withMatchedMiddleware(array $middleware): static
554
    {
555
        $route = clone $this;
556
557
        $route->matchedMiddleware = array_merge($this->matchedMiddleware ?? [], $middleware);
558
559
        return $route;
560
    }
561
562
    /**
563
     * @inheritDoc
564
     *
565
     * @return class-string<RouteDispatchedMiddleware>[]|null
566
     */
567
    public function getDispatchedMiddleware(): array|null
568
    {
569
        return $this->dispatchedMiddleware ?? null;
570
    }
571
572
    /**
573
     * @inheritDoc
574
     */
575
    public function setDispatchedMiddleware(array|null $middleware = null): static
576
    {
577
        $this->dispatchedMiddleware = $middleware;
578
579
        return $this;
580
    }
581
582
    /**
583
     * @inheritDoc
584
     */
585
    public function withDispatchedMiddleware(array $middleware): static
586
    {
587
        $route = clone $this;
588
589
        $route->dispatchedMiddleware = array_merge($this->dispatchedMiddleware ?? [], $middleware);
590
591
        return $route;
592
    }
593
594
    /**
595
     * @inheritDoc
596
     *
597
     * @return class-string<ThrowableCaughtMiddleware>[]|null
598
     */
599
    public function getExceptionMiddleware(): array|null
600
    {
601
        return $this->exceptionMiddleware ?? null;
602
    }
603
604
    /**
605
     * @inheritDoc
606
     */
607
    public function setExceptionMiddleware(array|null $middleware = null): static
608
    {
609
        $this->exceptionMiddleware = $middleware;
610
611
        return $this;
612
    }
613
614
    /**
615
     * @inheritDoc
616
     */
617
    public function withExceptionMiddleware(array $middleware): static
618
    {
619
        $route = clone $this;
620
621
        $route->exceptionMiddleware = array_merge($this->exceptionMiddleware ?? [], $middleware);
622
623
        return $route;
624
    }
625
626
    /**
627
     * @inheritDoc
628
     *
629
     * @return class-string<SendingResponseMiddleware>[]|null
630
     */
631
    public function getSendingMiddleware(): array|null
632
    {
633
        return $this->sendingMiddleware ?? null;
634
    }
635
636
    /**
637
     * @inheritDoc
638
     */
639
    public function setSendingMiddleware(array|null $middleware = null): static
640
    {
641
        $this->sendingMiddleware = $middleware;
642
643
        return $this;
644
    }
645
646
    /**
647
     * @inheritDoc
648
     */
649
    public function withSendingMiddleware(array $middleware): static
650
    {
651
        $route = clone $this;
652
653
        $route->sendingMiddleware = array_merge($this->sendingMiddleware ?? [], $middleware);
654
655
        return $route;
656
    }
657
658
    /**
659
     * @inheritDoc
660
     *
661
     * @return class-string<TerminatedMiddleware>[]|null
662
     */
663
    public function getTerminatedMiddleware(): array|null
664
    {
665
        return $this->terminatedMiddleware ?? null;
666
    }
667
668
    /**
669
     * @inheritDoc
670
     */
671
    public function setTerminatedMiddleware(array|null $middleware = null): static
672
    {
673
        $this->terminatedMiddleware = $middleware;
674
675
        return $this;
676
    }
677
678
    /**
679
     * @inheritDoc
680
     */
681
    public function withTerminatedMiddleware(array $middleware): static
682
    {
683
        $route = clone $this;
684
685
        $route->terminatedMiddleware = array_merge($this->terminatedMiddleware ?? [], $middleware);
686
687
        return $route;
688
    }
689
690
    /**
691
     * @inheritDoc
692
     */
693
    public function getRequestStruct(): string|null
694
    {
695
        return $this->requestStruct ?? null;
696
    }
697
698
    /**
699
     * @inheritDoc
700
     */
701
    public function setRequestStruct(string|null $requestStruct = null): static
702
    {
703
        assert($requestStruct === null || is_a($requestStruct, RequestStruct::class, true));
704
705
        $this->requestStruct = $requestStruct;
706
707
        return $this;
708
    }
709
710
    /**
711
     * Get the response struct.
712
     *
713
     * @return class-string<ResponseStruct>|null
714
     */
715
    public function getResponseStruct(): string|null
716
    {
717
        return $this->responseStruct ?? null;
718
    }
719
720
    /**
721
     * Set the response struct.
722
     *
723
     * @param class-string<ResponseStruct>|null $responseStruct The response struct
724
     *
725
     * @return static
726
     */
727
    public function setResponseStruct(string|null $responseStruct = null): static
728
    {
729
        assert($responseStruct === null || is_a($responseStruct, ResponseStruct::class, true));
730
731
        $this->responseStruct = $responseStruct;
732
733
        return $this;
734
    }
735
736
    /**
737
     * @inheritDoc
738
     */
739
    public function isDynamic(): bool
740
    {
741
        return $this->dynamic;
742
    }
743
744
    /**
745
     * @inheritDoc
746
     */
747
    public function setDynamic(bool $dynamic = true): static
748
    {
749
        $this->dynamic = $dynamic;
750
751
        return $this;
752
    }
753
754
    /**
755
     * @inheritDoc
756
     */
757
    public function isSecure(): bool
758
    {
759
        return $this->secure;
760
    }
761
762
    /**
763
     * @inheritDoc
764
     */
765
    public function setSecure(bool $secure = true): static
766
    {
767
        if ($secure) {
768
            $this->matchedMiddleware[] = SecureRouteMiddleware::class;
769
        }
770
771
        $this->secure = $secure;
772
773
        return $this;
774
    }
775
776
    /**
777
     * @inheritDoc
778
     */
779
    public function isRedirect(): bool
780
    {
781
        return $this->redirect;
782
    }
783
784
    /**
785
     * @inheritDoc
786
     */
787
    public function setRedirect(bool $redirect): static
788
    {
789
        if ($redirect) {
790
            $this->matchedMiddleware[] = RedirectRouteMiddleware::class;
791
        }
792
793
        $this->redirect = $redirect;
794
795
        return $this;
796
    }
797
}
798