Test Failed
Pull Request — master (#225)
by Alexander
12:36
created

Route::getPattern()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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 89
     * 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 89
     * It is useful to avoid invoking one of the parent group middleware for
55
     * a certain route.
56 77
     */
57
    public function __construct(
58 77
        array $methods,
59
        private string $pattern,
60
        private ?string $name = null,
61 9
        array|callable|string $action = null,
62
        array $middlewares = [],
63 9
        array $defaults = [],
64
        array $hosts = [],
65
        private bool $override = false,
66 4
        private array $disabledMiddlewares = [],
67
    ) {
68 4
        $this->setMethods($methods);
69
        $this->action = $action;
70
        $this->setMiddlewares($middlewares);
71 1
        $this->setHosts($hosts);
72
        $this->setDefaults($defaults);
73 1
    }
74
75
    /**
76 1
     * @return string[]
77
     */
78 1
    public function getMethods(): array
79
    {
80
        return $this->methods;
81 1
    }
82
83 1
    public function getAction(): array|callable|string|null
84
    {
85
        return $this->action;
86 9
    }
87
88 9
    public function getMiddlewares(): array
89
    {
90
        return $this->middlewares;
91
    }
92
93
    /**
94 89
     * @return string[]
95
     */
96 89
    public function getHosts(): array
97
    {
98
        return $this->hosts;
99 25
    }
100
101 25
    public function getDefaults(): array
102 25
    {
103 25
        return $this->defaults;
104
    }
105
106 24
    public function getPattern(): string
107
    {
108 24
        return $this->pattern;
109 24
    }
110 24
111
    public function getName(): string
112
    {
113 11
        return $this->name ??= (implode(', ', $this->methods) . ' ' . implode('|', $this->hosts) . $this->pattern);
114
    }
115 11
116
    public function isOverride(): bool
117
    {
118 15
        return $this->override;
119
    }
120 15
121 15
    public function getDisabledMiddlewares(): array
122
    {
123 15
        return $this->disabledMiddlewares;
124 15
    }
125
126 15
    /**
127 14
     * @return array[]|callable[]|string[]
128
     * @psalm-return list<array|callable|string>
129
     */
130
    public function getEnabledMiddlewares(): array
131 15
    {
132
        if ($this->enabledMiddlewaresCache !== null) {
133
            return $this->enabledMiddlewaresCache;
134
        }
135
136
        $this->enabledMiddlewaresCache = MiddlewareFilter::filter($this->middlewares, $this->disabledMiddlewares);
137 4
        if ($this->action !== null) {
138
            $this->enabledMiddlewaresCache[] = $this->action;
139 4
        }
140 4
141 4
        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
    public function setMethods(array $methods): self
145
    {
146
        if (empty($methods)) {
147
            throw new InvalidArgumentException('$methods cannot be empty.');
148
        }
149 5
        foreach ($methods as $method) {
150
            if (!is_string($method)) {
151 5
                throw new \InvalidArgumentException('Invalid $methods provided, list of string expected.');
152 5
            }
153 5
            $this->methods[] = $method;
154
        }
155
        return $this;
156
    }
157
158
    public function setHosts(array $hosts): self
159
    {
160 28
        $this->hosts = [];
161
        foreach ($hosts as $host) {
162 28
            if (!is_string($host)) {
163 1
                throw new \InvalidArgumentException('Invalid $hosts provided, list of string expected.');
164
            }
165
            $host = rtrim($host, '/');
166 27
167 27
            if ($host !== '' && !in_array($host, $this->hosts, true)) {
168 27
                $this->hosts[] = $host;
169 27
            }
170 27
        }
171
172 27
        return $this;
173
    }
174 27
175
    public function setAction(callable|array|string|null $action): self
176
    {
177
        $this->action = $action;
178
        return $this;
179
    }
180
181 27
    public function setMiddlewares(array $middlewares): self
182
    {
183 27
        $this->assertMiddlewares($middlewares);
184 1
        $this->middlewares = $middlewares;
185
        $this->enabledMiddlewaresCache = null;
186
        return $this;
187 26
    }
188 26
189 26
    public function setDefaults(array $defaults): self
190 26
    {
191 26
        /** @var mixed $value */
192
        foreach ($defaults as $key => $value) {
193 26
            if (!is_scalar($value) && !($value instanceof Stringable) && null !== $value) {
194
                throw new \InvalidArgumentException(
195 26
                    'Invalid $defaults provided, indexed array of scalar or `Stringable` or null expected.'
196
                );
197
            }
198
            $this->defaults[$key] = (string) $value;
199
        }
200
        return $this;
201 30
    }
202
203 30
    public function setPattern(string $pattern): self
204 30
    {
205 30
        $this->pattern = $pattern;
206 30
        return $this;
207
    }
208
209
    public function setName(?string $name): self
210
    {
211
        $this->name = $name;
212
        return $this;
213
    }
214 6
215
    public function setOverride(bool $override): self
216 6
    {
217 6
        $this->override = $override;
218 6
        return $this;
219 6
    }
220 6
221
    public function setDisabledMiddlewares(array $disabledMiddlewares): self
222 6
    {
223
        $this->disabledMiddlewares = $disabledMiddlewares;
224 6
        $this->enabledMiddlewaresCache = null;
225
        return $this;
226
    }
227
228
    public function __toString(): string
229
    {
230
        $result = $this->name === null
231
            ? ''
232
            : '[' . $this->name . '] ';
233
234
        if ($this->methods !== []) {
235
            $result .= implode(',', $this->methods) . ' ';
236
        }
237
238
        if (!empty($this->hosts)) {
239
            $quoted = array_map(static fn ($host) => preg_quote($host, '/'), $this->hosts);
240
241
            if (!preg_match('/' . implode('|', $quoted) . '/', $this->pattern)) {
242
                $result .= implode('|', $this->hosts);
243
            }
244
        }
245
246
        $result .= $this->pattern;
247 67
248
        return $result;
249 67
    }
250 67
251 36
    public function __debugInfo()
252 67
    {
253 67
        return [
254 67
            'name' => $this->name,
255 67
            'methods' => $this->methods,
256 67
            'pattern' => $this->pattern,
257 67
            'action' => $this->action,
258 67
            'hosts' => $this->hosts,
259 67
            'defaults' => $this->defaults,
260 67
            'override' => $this->override,
261 67
            'middlewares' => $this->middlewares,
262
            'disabledMiddlewares' => $this->disabledMiddlewares,
263
            'enabledMiddlewares' => $this->getEnabledMiddlewares(),
264 5
        ];
265
    }
266 5
267 2
    /**
268 3
     * @psalm-assert list<array|callable|string> $middlewares
269
     */
270 5
    private function assertMiddlewares(array $middlewares): void
271 5
    {
272
        /** @var mixed $middleware */
273
        foreach ($middlewares as $middleware) {
274 5
            if (is_string($middleware)) {
275 2
                continue;
276
            }
277 2
278 2
            if (is_callable($middleware) || is_array($middleware)) {
279
                continue;
280
            }
281
282 5
            throw new \InvalidArgumentException(
283
                'Invalid $middlewares provided, list of string or array or callable expected.'
284 5
            );
285
        }
286
    }
287
}
288