Passed
Pull Request — master (#210)
by Sergei
02:34
created

Group::withDispatcher()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
10
use function in_array;
11
12
final class Group
13
{
14
    /**
15
     * @var Group[]|Route[]
16
     */
17
    private array $items = [];
18
19
    /**
20
     * @var array[]|callable[]|string[]
21
     */
22
    private array $middlewareDefinitions = [];
23
24
    /**
25
     * @var string[]
26
     */
27
    private array $hosts = [];
28
    private ?string $namePrefix = null;
29
    private bool $routesAdded = false;
30
    private bool $middlewareAdded = false;
31
    private array $disabledMiddlewareDefinitions = [];
32
33
    /**
34
     * @var array|callable|string|null Middleware definition for CORS requests.
35
     */
36
    private $corsMiddleware = null;
37
38 34
    private function __construct(private ?string $prefix = null)
39
    {
40 34
    }
41
42
    /**
43
     * Create a new group instance.
44
     *
45
     * @param string|null $prefix URL prefix to prepend to all routes of the group.
46
     */
47 34
    public static function create(?string $prefix = null): self
48
    {
49 34
        return new self($prefix);
50
    }
51
52 26
    public function routes(self|Route ...$routes): self
53
    {
54 26
        if ($this->middlewareAdded) {
55 1
            throw new RuntimeException('routes() can not be used after prependMiddleware().');
56
        }
57 25
        $new = clone $this;
58 25
        foreach ($routes as $route) {
59 24
            $new->items[] = $route;
60
        }
61
62 25
        $new->routesAdded = true;
63
64 25
        return $new;
65
    }
66
67
    /**
68
     * Adds a middleware definition that handles CORS requests.
69
     * If set, routes for {@see Method::OPTIONS} request will be added automatically.
70
     *
71
     * @param array|callable|string|null $middlewareDefinition Middleware definition for CORS requests.
72
     */
73 8
    public function withCors(array|callable|string|null $middlewareDefinition): self
74
    {
75 8
        $group = clone $this;
76 8
        $group->corsMiddleware = $middlewareDefinition;
77
78 8
        return $group;
79
    }
80
81
    /**
82
     * Appends a handler middleware definition that should be invoked for a matched route.
83
     * First added handler will be executed first.
84
     */
85 11
    public function middleware(array|callable|string ...$middlewareDefinition): self
86
    {
87 11
        if ($this->routesAdded) {
88 1
            throw new RuntimeException('middleware() can not be used after routes().');
89
        }
90 10
        $new = clone $this;
91 10
        array_push(
92 10
            $new->middlewareDefinitions,
93 10
            ...array_values($middlewareDefinition)
94 10
        );
95 10
        return $new;
96
    }
97
98
    /**
99
     * Prepends a handler middleware definition that should be invoked for a matched route.
100
     * First added handler will be executed last.
101
     */
102 25
    public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
103
    {
104 25
        $new = clone $this;
105 25
        array_unshift(
106 25
            $new->middlewareDefinitions,
107 25
            ...array_values($middlewareDefinition)
108 25
        );
109 25
        $new->middlewareAdded = true;
110 25
        return $new;
111
    }
112
113 4
    public function namePrefix(string $namePrefix): self
114
    {
115 4
        $new = clone $this;
116 4
        $new->namePrefix = $namePrefix;
117 4
        return $new;
118
    }
119
120 2
    public function host(string $host): self
121
    {
122 2
        return $this->hosts($host);
123
    }
124
125 5
    public function hosts(string ...$hosts): self
126
    {
127 5
        $new = clone $this;
128
129 5
        foreach ($hosts as $host) {
130 4
            $host = rtrim($host, '/');
131
132 4
            if ($host !== '' && !in_array($host, $new->hosts, true)) {
133 4
                $new->hosts[] = $host;
134
            }
135
        }
136
137 5
        return $new;
138
    }
139
140
    /**
141
     * Excludes middleware from being invoked when action is handled.
142
     * It is useful to avoid invoking one of the parent group middleware for
143
     * a certain route.
144
     */
145 3
    public function disableMiddleware(mixed ...$middlewareDefinition): self
146
    {
147 3
        $new = clone $this;
148 3
        array_push(
149 3
            $new->disabledMiddlewareDefinitions,
150 3
            ...array_values($middlewareDefinition),
151 3
        );
152 3
        return $new;
153
    }
154
155
    /**
156
     * @psalm-template T as string
157
     * @psalm-param T $key
158
     * @psalm-return (
159
     *   T is ('prefix'|'namePrefix'|'host') ? string|null :
160
     *   (T is 'items' ? Group[]|Route[] :
161
     *     (T is 'hosts' ? array<array-key, string> :
162
     *       (T is ('hasCorsMiddleware') ? bool :
163
     *         (T is 'middlewareDefinitions' ? list<array|callable|string> :
164
     *           (T is 'corsMiddleware' ? array|callable|string|null : mixed)
165
     *         )
166
     *       )
167
     *     )
168
     *   )
169
     * )
170
     */
171 30
    public function getData(string $key): mixed
172
    {
173 30
        return match ($key) {
174 30
            'prefix' => $this->prefix,
175 30
            'namePrefix' => $this->namePrefix,
176 30
            'host' => $this->hosts[0] ?? null,
177 30
            'hosts' => $this->hosts,
178 30
            'corsMiddleware' => $this->corsMiddleware,
179 30
            'items' => $this->items,
180 30
            'hasCorsMiddleware' => $this->corsMiddleware !== null,
181 30
            'middlewareDefinitions' => $this->getMiddlewareDefinitions(),
182 30
            default => throw new InvalidArgumentException('Unknown data key: ' . $key),
183 30
        };
184
    }
185
186 21
    private function getMiddlewareDefinitions(): array
187
    {
188
        /** @var mixed $definition */
189 21
        foreach ($this->middlewareDefinitions as $index => $definition) {
190 11
            if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
191 2
                unset($this->middlewareDefinitions[$index]);
192
            }
193
        }
194
195 21
        return array_values($this->middlewareDefinitions);
196
    }
197
}
198