Passed
Pull Request — master (#222)
by Sergei
02:37
created

Group::getEnabledMiddlewares()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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