Passed
Pull Request — master (#138)
by Rustam
02:48
created

Route::name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Yiisoft\Http\Method;
10
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
11
12
/**
13
 * Route defines a mapping from URL to callback / name and vice versa.
14
 */
15
final class Route
16
{
17
    private ?string $name = null;
18
    /** @var string[] */
19
    private array $methods;
20
    private string $pattern;
21
    private ?string $host = null;
22
    private bool $override = false;
23
    private ?MiddlewareDispatcher $dispatcher;
24
    private bool $actionAdded = false;
25
    private array $middlewareDefinitions = [];
26
    private array $disabledMiddlewareDefinitions = [];
27
    private array $defaults = [];
28
29 58
    private function __construct(?MiddlewareDispatcher $dispatcher = null)
30
    {
31 58
        $this->dispatcher = $dispatcher;
32 58
    }
33
34 7
    public function injectDispatcher(MiddlewareDispatcher $dispatcher): void
35
    {
36 7
        $this->dispatcher = $dispatcher;
37 7
    }
38
39
    /**
40
     * @return self
41
     */
42 5
    public function withDispatcher(MiddlewareDispatcher $dispatcher): self
43
    {
44 5
        $route = clone $this;
45 5
        $route->dispatcher = $dispatcher;
46 5
        return $route;
47
    }
48
49
    /**
50
     * @param string $pattern
51
     * @param MiddlewareDispatcher|null $dispatcher
52
     *
53
     * @return self
54
     */
55 47
    public static function get(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
56
    {
57 47
        return self::methods([Method::GET], $pattern, $dispatcher);
58
    }
59
60
    /**
61
     * @param string $pattern
62
     * @param MiddlewareDispatcher|null $dispatcher
63
     *
64
     * @return self
65
     */
66 9
    public static function post(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
67
    {
68 9
        return self::methods([Method::POST], $pattern, $dispatcher);
69
    }
70
71
    /**
72
     * @param string $pattern
73
     * @param MiddlewareDispatcher|null $dispatcher
74
     *
75
     * @return self
76
     */
77 4
    public static function put(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
78
    {
79 4
        return self::methods([Method::PUT], $pattern, $dispatcher);
80
    }
81
82
    /**
83
     * @param string $pattern
84
     * @param MiddlewareDispatcher|null $dispatcher
85
     *
86
     * @return self
87
     */
88 1
    public static function delete(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
89
    {
90 1
        return self::methods([Method::DELETE], $pattern, $dispatcher);
91
    }
92
93
    /**
94
     * @param string $pattern
95
     * @param MiddlewareDispatcher|null $dispatcher
96
     *
97
     * @return self
98
     */
99 1
    public static function patch(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
100
    {
101 1
        return self::methods([Method::PATCH], $pattern, $dispatcher);
102
    }
103
104
    /**
105
     * @param string $pattern
106
     * @param MiddlewareDispatcher|null $dispatcher
107
     *
108
     * @return self
109
     */
110 1
    public static function head(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
111
    {
112 1
        return self::methods([Method::HEAD], $pattern, $dispatcher);
113
    }
114
115
    /**
116
     * @param string $pattern
117
     * @param MiddlewareDispatcher|null $dispatcher
118
     *
119
     * @return self
120
     */
121 7
    public static function options(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
122
    {
123 7
        return self::methods([Method::OPTIONS], $pattern, $dispatcher);
124
    }
125
126
    /**
127
     * @param array $methods
128
     * @param string $pattern
129
     * @param MiddlewareDispatcher|null $dispatcher
130
     *
131
     * @return self
132
     */
133 58
    public static function methods(
134
        array $methods,
135
        string $pattern,
136
        ?MiddlewareDispatcher $dispatcher = null
137
    ): self {
138 58
        $route = new self($dispatcher);
139 58
        $route->methods = $methods;
140 58
        $route->pattern = $pattern;
141
142 58
        return $route;
143
    }
144
145
    /**
146
     * @return self
147
     */
148 18
    public function name(string $name): self
149
    {
150 18
        $route = clone $this;
151 18
        $route->name = $name;
152 18
        return $route;
153
    }
154
155
    /**
156
     * @return self
157
     */
158 19
    public function pattern(string $pattern): self
159
    {
160 19
        $new = clone $this;
161 19
        $new->pattern = $pattern;
162 19
        return $new;
163
    }
164
165
    /**
166
     * @return self
167
     */
168 5
    public function host(string $host): self
169
    {
170 5
        $route = clone $this;
171 5
        $route->host = rtrim($host, '/');
172 5
        return $route;
173
    }
174
175
    /**
176
     * Marks route as override. When added it will replace existing route with the same name.
177
     *
178
     * @return self
179
     */
180 2
    public function override(): self
181
    {
182 2
        $route = clone $this;
183 2
        $route->override = true;
184 2
        return $route;
185
    }
186
187
    /**
188
     * Parameter default values indexed by parameter names.
189
     *
190
     * @param array $defaults
191
     *
192
     * @return self
193
     */
194 1
    public function defaults(array $defaults): self
195
    {
196 1
        $route = clone $this;
197 1
        $route->defaults = $defaults;
198 1
        return $route;
199
    }
200
201
    /**
202
     * Appends a handler middleware definition that should be invoked for a matched route.
203
     * First added handler will be executed first.
204
     *
205
     * @param mixed $middlewareDefinition
206
     *
207
     * @return self
208
     */
209 11
    public function middleware($middlewareDefinition): self
210
    {
211 11
        if ($this->actionAdded) {
212
            throw new RuntimeException('middleware() can not be used after action().');
213
        }
214 11
        $route = clone $this;
215 11
        $route->middlewareDefinitions[] = $middlewareDefinition;
216 11
        return $route;
217
    }
218
219
    /**
220
     * Prepends a handler middleware definition that should be invoked for a matched route.
221
     * Last added handler will be executed first.
222
     *
223
     * @param mixed $middlewareDefinition
224
     *
225
     * @return self
226
     */
227 11
    public function prependMiddleware($middlewareDefinition): self
228
    {
229 11
        if (!$this->actionAdded) {
230
            throw new RuntimeException('prependMiddleware() can not be used before action().');
231
        }
232 11
        $route = clone $this;
233 11
        array_unshift($route->middlewareDefinitions, $middlewareDefinition);
234 11
        return $route;
235
    }
236
237
    /**
238
     * Appends action handler. It is a primary middleware definition that should be invoked last for a matched route.
239
     *
240
     * @param mixed $middlewareDefinition
241
     *
242
     * @return self
243
     */
244 13
    public function action($middlewareDefinition): self
245
    {
246 13
        $route = clone $this;
247 13
        $route->middlewareDefinitions[] = $middlewareDefinition;
248 13
        $route->actionAdded = true;
249 13
        return $route;
250
    }
251
252
    /**
253
     * Excludes middleware from being invoked when action is handled.
254
     * It is useful to avoid invoking one of the parent group middleware for
255
     * a certain route.
256
     *
257
     * @param mixed $middlewareDefinition
258
     *
259
     * @return self
260
     */
261
    public function disableMiddleware($middlewareDefinition): self
262
    {
263
        $route = clone $this;
264
        $route->disabledMiddlewareDefinitions[] = $middlewareDefinition;
265
        return $route;
266
    }
267
268
    /**
269
     * @param string $key
270
     *
271
     * @return mixed
272
     *
273
     * @internal
274
     */
275 47
    public function getData(string $key)
276
    {
277 47
        switch ($key) {
278 47
            case 'name':
279 22
                return $this->name ?? (implode(', ', $this->methods) . ' ' . $this->host . $this->pattern);
280 44
            case 'pattern':
281 20
                return $this->pattern;
282 42
            case 'host':
283 8
                return $this->host;
284 40
            case 'methods':
285 27
                return $this->methods;
286 31
            case 'defaults':
287 1
                return $this->defaults;
288 30
            case 'override':
289 4
                return $this->override;
290 29
            case 'dispatcherWithMiddlewares':
291 12
                return $this->getDispatcherWithMiddlewares();
292 27
            case 'hasMiddlewares':
293 18
                return $this->middlewareDefinitions !== [];
294 27
            case 'hasDispatcher':
295 26
                return $this->dispatcher !== null;
296
            default:
297 1
                throw new InvalidArgumentException('Unknown data key: ' . $key);
298
        }
299
    }
300
301 3
    public function __toString(): string
302
    {
303 3
        $result = '';
304
305 3
        if ($this->name !== null) {
306 2
            $result .= '[' . $this->name . '] ';
307
        }
308
309 3
        if ($this->methods !== []) {
310 3
            $result .= implode(',', $this->methods) . ' ';
311
        }
312 3
        if ($this->host !== null && strrpos($this->pattern, $this->host) === false) {
313 1
            $result .= $this->host;
314
        }
315 3
        $result .= $this->pattern;
316
317 3
        return $result;
318
    }
319
320
    public function __debugInfo()
321
    {
322
        return [
323
            'name' => $this->name,
324
            'methods' => $this->methods,
325
            'pattern' => $this->pattern,
326
            'host' => $this->host,
327
            'defaults' => $this->defaults,
328
            'override' => $this->override,
329
            'actionAdded' => $this->actionAdded,
330
            'middlewareDefinitions' => $this->middlewareDefinitions,
331
            'disabledMiddlewareDefinitions' => $this->disabledMiddlewareDefinitions,
332
            'middlewareDispatcher' => $this->dispatcher,
333
        ];
334
    }
335
336
    /**
337
     * @return MiddlewareDispatcher
338
     */
339 12
    private function getDispatcherWithMiddlewares(): MiddlewareDispatcher
340
    {
341 12
        if ($this->dispatcher->hasMiddlewares()) {
0 ignored issues
show
Bug introduced by
The method hasMiddlewares() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

341
        if ($this->dispatcher->/** @scrutinizer ignore-call */ hasMiddlewares()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
342
            return $this->dispatcher;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->dispatcher could return the type null which is incompatible with the type-hinted return Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher. Consider adding an additional type-check to rule them out.
Loading history...
343
        }
344
345 12
        foreach ($this->middlewareDefinitions as $index => $definition) {
346 12
            if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
347
                unset($this->middlewareDefinitions[$index]);
348
            }
349
        }
350
351 12
        return $this->dispatcher = $this->dispatcher->withMiddlewares($this->middlewareDefinitions);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->dispatcher...>middlewareDefinitions) could return the type null which is incompatible with the type-hinted return Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher. Consider adding an additional type-check to rule them out.
Loading history...
352
    }
353
}
354