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

Route::patch()   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 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Router;
6
7
use RuntimeException;
8
use Yiisoft\Http\Method;
9
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
10
11
/**
12
 * Route defines a mapping from URL to callback / name and vice versa.
13
 */
14
final class Route
15
{
16
    public const NAME = 'name';
17
    public const METHODS = 'methods';
18
    public const PATTERN = 'pattern';
19
    public const HOST = 'host';
20
    public const DEFAULTS = 'defaults';
21
    public const OVERRIDE = 'override';
22
23
    private ?string $name = null;
24
    /** @var string[] */
25
    private array $methods;
26
    private string $pattern;
27
    private ?string $host = null;
28
    private bool $override = false;
29
    private ?MiddlewareDispatcher $dispatcher;
30
    private bool $actionAdded = false;
31
    private array $middlewareDefinitions = [];
32
    private array $disabledMiddlewareDefinitions = [];
33
    private array $defaults = [];
34
35 50
    private function __construct(?MiddlewareDispatcher $dispatcher = null)
36
    {
37 50
        $this->dispatcher = $dispatcher;
38 50
    }
39
40 7
    public function injectDispatcher(MiddlewareDispatcher $dispatcher): void
41
    {
42 7
        $this->dispatcher = $dispatcher;
43 7
    }
44
45
    /**
46
     * @return self
47
     */
48 5
    public function withDispatcher(MiddlewareDispatcher $dispatcher): self
49
    {
50 5
        $route = clone $this;
51 5
        $route->dispatcher = $dispatcher;
52 5
        return $route;
53
    }
54
55 12
    public function getDispatcherWithMiddlewares(): MiddlewareDispatcher
56
    {
57 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

57
        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...
58
            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...
59
        }
60
61 12
        foreach ($this->middlewareDefinitions as $index => $definition) {
62 12
            if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
63
                unset($this->middlewareDefinitions[$index]);
64
            }
65
        }
66
67 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...
68
    }
69
70 26
    public function hasDispatcher(): bool
71
    {
72 26
        return $this->dispatcher !== null;
73
    }
74
75
    /**
76
     * @param string $pattern
77
     * @param MiddlewareDispatcher|null $dispatcher
78
     *
79
     * @return self
80
     */
81 39
    public static function get(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
82
    {
83 39
        return self::methods([Method::GET], $pattern, $dispatcher);
84
    }
85
86
    /**
87
     * @param string $pattern
88
     * @param MiddlewareDispatcher|null $dispatcher
89
     *
90
     * @return self
91
     */
92 9
    public static function post(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
93
    {
94 9
        return self::methods([Method::POST], $pattern, $dispatcher);
95
    }
96
97
    /**
98
     * @param string $pattern
99
     * @param MiddlewareDispatcher|null $dispatcher
100
     *
101
     * @return self
102
     */
103 4
    public static function put(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
104
    {
105 4
        return self::methods([Method::PUT], $pattern, $dispatcher);
106
    }
107
108
    /**
109
     * @param string $pattern
110
     * @param MiddlewareDispatcher|null $dispatcher
111
     *
112
     * @return self
113
     */
114 1
    public static function delete(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
115
    {
116 1
        return self::methods([Method::DELETE], $pattern, $dispatcher);
117
    }
118
119
    /**
120
     * @param string $pattern
121
     * @param MiddlewareDispatcher|null $dispatcher
122
     *
123
     * @return self
124
     */
125 1
    public static function patch(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
126
    {
127 1
        return self::methods([Method::PATCH], $pattern, $dispatcher);
128
    }
129
130
    /**
131
     * @param string $pattern
132
     * @param MiddlewareDispatcher|null $dispatcher
133
     *
134
     * @return self
135
     */
136 1
    public static function head(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
137
    {
138 1
        return self::methods([Method::HEAD], $pattern, $dispatcher);
139
    }
140
141
    /**
142
     * @param string $pattern
143
     * @param MiddlewareDispatcher|null $dispatcher
144
     *
145
     * @return self
146
     */
147 7
    public static function options(string $pattern, ?MiddlewareDispatcher $dispatcher = null): self
148
    {
149 7
        return self::methods([Method::OPTIONS], $pattern, $dispatcher);
150
    }
151
152
    /**
153
     * @param array $methods
154
     * @param string $pattern
155
     * @param MiddlewareDispatcher|null $dispatcher
156
     *
157
     * @return self
158
     */
159 50
    public static function methods(
160
        array $methods,
161
        string $pattern,
162
        ?MiddlewareDispatcher $dispatcher = null
163
    ): self {
164 50
        $route = new self($dispatcher);
165 50
        $route->methods = $methods;
166 50
        $route->pattern = $pattern;
167
168 50
        return $route;
169
    }
170
171
    /**
172
     * @return self
173
     */
174 19
    public function name(string $name): self
175
    {
176 19
        $route = clone $this;
177 19
        $route->name = $name;
178 19
        return $route;
179
    }
180
181
    /**
182
     * @return self
183
     */
184 19
    public function pattern(string $pattern): self
185
    {
186 19
        $new = clone $this;
187 19
        $new->pattern = $pattern;
188 19
        return $new;
189
    }
190
191
    /**
192
     * @return self
193
     */
194 4
    public function host(string $host): self
195
    {
196 4
        $route = clone $this;
197 4
        $route->host = rtrim($host, '/');
198 4
        return $route;
199
    }
200
201
    /**
202
     * Marks route as override. When added it will replace existing route with the same name.
203
     *
204
     * @return self
205
     */
206 2
    public function override(): self
207
    {
208 2
        $route = clone $this;
209 2
        $route->override = true;
210 2
        return $route;
211
    }
212
213
    /**
214
     * Parameter default values indexed by parameter names.
215
     *
216
     * @param array $defaults
217
     *
218
     * @return self
219
     */
220 1
    public function defaults(array $defaults): self
221
    {
222 1
        $route = clone $this;
223 1
        $route->defaults = $defaults;
224 1
        return $route;
225
    }
226
227
    /**
228
     * Appends a handler middleware definition that should be invoked for a matched route.
229
     * First added handler will be executed first.
230
     *
231
     * @param mixed $middlewareDefinition
232
     *
233
     * @return self
234
     */
235 11
    public function middleware($middlewareDefinition): self
236
    {
237 11
        if ($this->actionAdded) {
238
            throw new RuntimeException('middleware() can not be used after action().');
239
        }
240 11
        $route = clone $this;
241 11
        $route->middlewareDefinitions[] = $middlewareDefinition;
242 11
        return $route;
243
    }
244
245
    /**
246
     * Prepends a handler middleware definition that should be invoked for a matched route.
247
     * Last added handler will be executed first.
248
     *
249
     * @param mixed $middlewareDefinition
250
     *
251
     * @return self
252
     */
253 11
    public function prependMiddleware($middlewareDefinition): self
254
    {
255 11
        if (!$this->actionAdded) {
256
            throw new RuntimeException('prependMiddleware() can not be used before action().');
257
        }
258 11
        $route = clone $this;
259 11
        array_unshift($route->middlewareDefinitions, $middlewareDefinition);
260 11
        return $route;
261
    }
262
263
    /**
264
     * Appends action handler. It is a primary middleware definition that should be invoked last for a matched route.
265
     *
266
     * @param mixed $middlewareDefinition
267
     *
268
     * @return self
269
     */
270 13
    public function action($middlewareDefinition): self
271
    {
272 13
        $route = clone $this;
273 13
        $route->middlewareDefinitions[] = $middlewareDefinition;
274 13
        $route->actionAdded = true;
275 13
        return $route;
276
    }
277
278
    /**
279
     * Excludes middleware from being invoked when action is handled.
280
     * It is useful to avoid invoking one of the parent group middleware for
281
     * a certain route.
282
     *
283
     * @param mixed $middlewareDefinition
284
     *
285
     * @return self
286
     */
287
    public function disableMiddleware($middlewareDefinition): self
288
    {
289
        $route = clone $this;
290
        $route->disabledMiddlewareDefinitions[] = $middlewareDefinition;
291
        return $route;
292
    }
293
294 3
    public function __toString(): string
295
    {
296 3
        $result = '';
297
298 3
        if ($this->name !== null) {
299 2
            $result .= '[' . $this->name . '] ';
300
        }
301
302 3
        if ($this->methods !== []) {
303 3
            $result .= implode(',', $this->methods) . ' ';
304
        }
305 3
        if ($this->host !== null && strrpos($this->pattern, $this->host) === false) {
306 1
            $result .= $this->host;
307
        }
308 3
        $result .= $this->pattern;
309
310 3
        return $result;
311
    }
312
313
    /**
314
     * @param string $key
315
     *
316
     * @return mixed
317
     */
318 34
    public function getData(string $key)
319
    {
320 34
        $dataMap = $this->getDataMap();
321
322 34
        return $dataMap[$key] ?? null;
323
    }
324
325
    /**
326
     * @return array
327
     */
328 34
    private function getDataMap(): array
329
    {
330
        return [
331 34
            self::NAME => $this->name ?? (implode(', ', $this->methods) . ' ' . $this->host . $this->pattern),
332 34
            self::PATTERN => $this->pattern,
333 34
            self::HOST => $this->host,
334 34
            self::METHODS => $this->methods,
335 34
            self::DEFAULTS => $this->defaults,
336 34
            self::OVERRIDE => $this->override,
337
        ];
338
    }
339
340 18
    public function hasMiddlewares(): bool
341
    {
342 18
        return $this->middlewareDefinitions !== [];
343
    }
344
345
    public function __debugInfo()
346
    {
347
        return [
348
            'name' => $this->name,
349
            'methods' => $this->methods,
350
            'pattern' => $this->pattern,
351
            'host' => $this->host,
352
            'defaults' => $this->defaults,
353
            'override' => $this->override,
354
            'actionAdded' => $this->actionAdded,
355
            'middlewareDefinitions' => $this->middlewareDefinitions,
356
            'disabledMiddlewareDefinitions' => $this->disabledMiddlewareDefinitions,
357
            'middlewareDispatcher' => $this->dispatcher,
358
        ];
359
    }
360
}
361