Passed
Pull Request — master (#225)
by Rustam
11:11 queued 08:15
created

Route::setName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
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 Stringable;
9
use Yiisoft\Router\Internal\MiddlewareFilter;
10
11
/**
12
 * Route defines a mapping from URL to callback / name and vice versa.
13
 */
14
final class Route implements Stringable
15
{
16
    /**
17
     * @var string[]
18
     * @psalm-var array<array-key, string>
19
     */
20
    private array $methods = [];
21
22
    /**
23
     * @var string[]
24
     */
25
    private array $hosts = [];
26
27
    /**
28
     * @var array|callable|string|null
29
     */
30
    private $action = null;
31
32
    /**
33
     * @var array[]|callable[]|string[]
34
     * @psalm-var list<array|callable|string>
35
     */
36
    private array $middlewares = [];
37
38
    /**
39
     * @psalm-var list<array|callable|string>|null
40
     */
41
    private ?array $enabledMiddlewaresCache = null;
42
43
    /**
44
     * @var array<array-key,string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,string>.
Loading history...
45
     */
46
    private array $defaults = [];
47
48
    /**
49
     * @param array|callable|string|null $action Action handler. It is a primary middleware definition that
50
     * should be invoked last for a matched route.
51
     * @param array<array-key,scalar|Stringable|null> $defaults Parameter default values indexed by parameter names.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key,scalar|Stringable|null> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key,scalar|Stringable|null>.
Loading history...
52
     * @param bool $override Marks route as override. When added it will replace existing route with the same name.
53
     * @param array $disabledMiddlewares Excludes middleware from being invoked when action is handled.
54
     * It is useful to avoid invoking one of the parent group middleware for
55
     * a certain route.
56
     */
57 102
    public function __construct(
58
        array $methods,
59
        private string $pattern,
60
        private ?string $name = null,
61
        array|callable|string $action = null,
62
        array $middlewares = [],
63
        array $defaults = [],
64
        array $hosts = [],
65
        private bool $override = false,
66
        private array $disabledMiddlewares = [],
67
    ) {
68 102
        $this->setMethods($methods);
69 100
        $this->action = $action;
70 100
        $this->setMiddlewares($middlewares);
71 99
        $this->setHosts($hosts);
72 98
        $this->setDefaults($defaults);
73
    }
74
75
    /**
76
     * @return string[]
77
     */
78 32
    public function getMethods(): array
79
    {
80 32
        return $this->methods;
81
    }
82
83 26
    public function getAction(): array|callable|string|null
84
    {
85 26
        return $this->action;
86
    }
87
88 31
    public function getMiddlewares(): array
89
    {
90 31
        return $this->middlewares;
91
    }
92
93
    /**
94
     * @return string[]
95
     */
96 16
    public function getHosts(): array
97
    {
98 16
        return $this->hosts;
99
    }
100
101 2
    public function getDefaults(): array
102
    {
103 2
        return $this->defaults;
104
    }
105
106 27
    public function getPattern(): string
107
    {
108 27
        return $this->pattern;
109
    }
110
111 39
    public function getName(): string
112
    {
113 39
        return $this->name ??= (implode(', ', $this->methods) . ' ' . implode('|', $this->hosts) . $this->pattern);
114
    }
115
116 6
    public function isOverride(): bool
117
    {
118 6
        return $this->override;
119
    }
120
121 1
    public function getDisabledMiddlewares(): array
122
    {
123 1
        return $this->disabledMiddlewares;
124
    }
125
126
    /**
127
     * @return array[]|callable[]|string[]
128
     * @psalm-return list<array|callable|string>
129
     */
130 30
    public function getEnabledMiddlewares(): array
131
    {
132 30
        if ($this->enabledMiddlewaresCache !== null) {
133 1
            return $this->enabledMiddlewaresCache;
134
        }
135
136 30
        $this->enabledMiddlewaresCache = MiddlewareFilter::filter($this->middlewares, $this->disabledMiddlewares);
137 30
        if ($this->action !== null) {
138 20
            $this->enabledMiddlewaresCache[] = $this->action;
139
        }
140
141 30
        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...
142
    }
143
144 102
    public function setMethods(array $methods): self
145
    {
146 102
        if (empty($methods)) {
147 1
            throw new InvalidArgumentException('$methods cannot be empty.');
148
        }
149 101
        foreach ($methods as $method) {
150 101
            if (!is_string($method)) {
151 1
                throw new \InvalidArgumentException('Invalid $methods provided, list of string expected.');
152
            }
153 100
            $this->methods[] = $method;
154
        }
155 100
        return $this;
156
    }
157
158 99
    public function setHosts(array $hosts): self
159
    {
160 99
        $this->hosts = [];
161 99
        foreach ($hosts as $host) {
162 19
            if (!is_string($host)) {
163 1
                throw new \InvalidArgumentException('Invalid $hosts provided, list of string expected.');
164
            }
165 19
            $host = rtrim($host, '/');
166
167 19
            if ($host !== '' && !in_array($host, $this->hosts, true)) {
168 19
                $this->hosts[] = $host;
169
            }
170
        }
171
172 98
        return $this;
173
    }
174
175 7
    public function setAction(callable|array|string|null $action): self
176
    {
177 7
        $this->action = $action;
178 7
        return $this;
179
    }
180
181 100
    public function setMiddlewares(array $middlewares): self
182
    {
183 100
        $this->assertMiddlewares($middlewares);
184 99
        $this->middlewares = $middlewares;
185 99
        $this->enabledMiddlewaresCache = null;
186 99
        return $this;
187
    }
188
189 98
    public function setDefaults(array $defaults): self
190
    {
191
        /** @var mixed $value */
192 98
        foreach ($defaults as $key => $value) {
193 6
            if (!is_scalar($value) && !($value instanceof Stringable) && null !== $value) {
194 1
                throw new \InvalidArgumentException(
195 1
                    'Invalid $defaults provided, indexed array of scalar or `Stringable` or null expected.'
196 1
                );
197
            }
198 6
            $this->defaults[$key] = (string) $value;
199
        }
200 97
        return $this;
201
    }
202
203 23
    public function setPattern(string $pattern): self
204
    {
205 23
        $this->pattern = $pattern;
206 23
        return $this;
207
    }
208
209 17
    public function setName(?string $name): self
210
    {
211 17
        $this->name = $name;
212 17
        return $this;
213
    }
214
215 1
    public function setOverride(bool $override): self
216
    {
217 1
        $this->override = $override;
218 1
        return $this;
219
    }
220
221 2
    public function setDisabledMiddlewares(array $disabledMiddlewares): self
222
    {
223 2
        $this->disabledMiddlewares = $disabledMiddlewares;
224 2
        $this->enabledMiddlewaresCache = null;
225 2
        return $this;
226
    }
227
228 8
    public function __toString(): string
229
    {
230 8
        $result = $this->name === null
231 2
            ? ''
232 6
            : '[' . $this->name . '] ';
233
234 8
        if ($this->methods !== []) {
235 8
            $result .= implode(',', $this->methods) . ' ';
236
        }
237
238 8
        if (!empty($this->hosts)) {
239 4
            $quoted = array_map(static fn ($host) => preg_quote($host, '/'), $this->hosts);
240
241 4
            if (!preg_match('/' . implode('|', $quoted) . '/', $this->pattern)) {
242 4
                $result .= implode('|', $this->hosts);
243
            }
244
        }
245
246 8
        $result .= $this->pattern;
247
248 8
        return $result;
249
    }
250
251 3
    public function __debugInfo()
252
    {
253 3
        return [
254 3
            'name' => $this->name,
255 3
            'methods' => $this->methods,
256 3
            'pattern' => $this->pattern,
257 3
            'action' => $this->action,
258 3
            'hosts' => $this->hosts,
259 3
            'defaults' => $this->defaults,
260 3
            'override' => $this->override,
261 3
            'middlewares' => $this->middlewares,
262 3
            'disabledMiddlewares' => $this->disabledMiddlewares,
263 3
            'enabledMiddlewares' => $this->getEnabledMiddlewares(),
264 3
        ];
265
    }
266
267
    /**
268
     * @psalm-assert list<array|callable|string> $middlewares
269
     */
270 100
    private function assertMiddlewares(array $middlewares): void
271
    {
272
        /** @var mixed $middleware */
273 100
        foreach ($middlewares as $middleware) {
274 36
            if (is_string($middleware)) {
275 15
                continue;
276
            }
277
278 21
            if (is_callable($middleware) || is_array($middleware)) {
279 21
                continue;
280
            }
281
282 1
            throw new \InvalidArgumentException(
283 1
                'Invalid $middlewares provided, list of string or array or callable expected.'
284 1
            );
285
        }
286
    }
287
}
288