Passed
Push — master ( f8dde9...fe9361 )
by Alexander
10:36
created

Route.php$2 ➔ process()   A

Complexity

Conditions 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
cc 2
crap 2
rs 10
1
<?php
2
3
namespace Yiisoft\Router;
4
5
use InvalidArgumentException;
6
use Psr\Http\Server\MiddlewareInterface;
7
use Yiisoft\Http\Method;
8
9
/**
10
 * Route defines a mapping from URL to callback / name and vice versa
11
 */
12
final class Route
13
{
14
    private ?string $name = null;
15
    /** @var string[] */
16
    private array $methods;
17
    private string $pattern;
18
    private ?string $host = null;
19
    private ?DispatcherInterface $dispatcher = null;
20
21
    /**
22
     * @var callable[]|string[]|array[]
23
     */
24
    private array $middlewares = [];
25
    private array $defaults = [];
26
27
    private function __construct(?DispatcherInterface $dispatcher = null)
28
    {
29
        $this->dispatcher = $dispatcher;
30
    }
31
32
    public function withDispatcher(DispatcherInterface $dispatcher): self
33
    {
34
        $route = clone $this;
35
        $route->dispatcher = $dispatcher;
36
        return $route;
37
    }
38 44
39
    public function getDispatcherWithMiddlewares(): DispatcherInterface
40 44
    {
41 44
        return $this->dispatcher->withMiddlewares($this->middlewares);
0 ignored issues
show
Bug introduced by
The method withMiddlewares() 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

41
        return $this->dispatcher->/** @scrutinizer ignore-call */ withMiddlewares($this->middlewares);

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...
42
    }
43 7
44
    public function hasDispatcher(): bool
45 7
    {
46 7
        return $this->dispatcher !== null;
47 7
    }
48
49
    /**
50 10
     * @param string $pattern
51
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
52 10
     * @param DispatcherInterface|null $dispatcher
53
     * @return self
54
     */
55
    public static function get(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
56
    {
57
        return self::methods([Method::GET], $pattern, $middleware, $dispatcher);
58
    }
59
60
    /**
61 34
     * @param string $pattern
62
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
63 34
     * @param DispatcherInterface|null $dispatcher
64
     * @return self
65
     */
66
    public static function post(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
67
    {
68
        return self::methods([Method::POST], $pattern, $middleware, $dispatcher);
69
    }
70
71
    /**
72 4
     * @param string $pattern
73
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
74 4
     * @param DispatcherInterface|null $dispatcher
75
     * @return self
76
     */
77
    public static function put(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
78
    {
79
        return self::methods([Method::PUT], $pattern, $middleware, $dispatcher);
80
    }
81
82
    /**
83 1
     * @param string $pattern
84
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
85 1
     * @param DispatcherInterface|null $dispatcher
86
     * @return self
87
     */
88
    public static function delete(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
89
    {
90
        return self::methods([Method::DELETE], $pattern, $middleware, $dispatcher);
91
    }
92
93
    /**
94 1
     * @param string $pattern
95
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
96 1
     * @param DispatcherInterface|null $dispatcher
97
     * @return self
98
     */
99
    public static function patch(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
100
    {
101
        return self::methods([Method::PATCH], $pattern, $middleware, $dispatcher);
102
    }
103
104
    /**
105 1
     * @param string $pattern
106
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
107 1
     * @param DispatcherInterface|null $dispatcher
108
     * @return self
109
     */
110
    public static function head(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
111
    {
112
        return self::methods([Method::HEAD], $pattern, $middleware, $dispatcher);
113
    }
114
115
    /**
116 1
     * @param string $pattern
117
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
118 1
     * @param DispatcherInterface|null $dispatcher
119
     * @return self
120
     */
121
    public static function options(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
122
    {
123
        return self::methods([Method::OPTIONS], $pattern, $middleware, $dispatcher);
124
    }
125
126
    /**
127 1
     * @param array $methods
128
     * @param string $pattern
129 1
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
130
     * @param DispatcherInterface|null $dispatcher
131
     * @return self
132
     */
133
    public static function methods(
134
        array $methods,
135
        string $pattern,
136
        $middleware = null,
137
        ?DispatcherInterface $dispatcher = null
138
    ): self {
139 44
        $route = new self($dispatcher);
140
        $route->methods = $methods;
141
        $route->pattern = $pattern;
142
        if ($middleware !== null) {
143
            $route->validateMiddleware($middleware);
144
            $route->middlewares[] = $middleware;
145 44
        }
146 44
        return $route;
147 44
    }
148 44
149 10
    /**
150 4
     * @param string $pattern
151
     * @param callable|string|array|null $middleware primary route handler {@see addMiddleware()}
152 38
     * @param DispatcherInterface|null $dispatcher
153
     * @return self
154
     */
155
    public static function anyMethod(string $pattern, $middleware = null, ?DispatcherInterface $dispatcher = null): self
156
    {
157
        return self::methods(Method::ANY, $pattern, $middleware, $dispatcher);
158
    }
159
160
    public function name(string $name): self
161 1
    {
162
        $route = clone $this;
163 1
        $route->name = $name;
164
        return $route;
165
    }
166 8
167
    public function pattern(string $pattern): self
168 8
    {
169 8
        $new = clone $this;
170 8
        $new->pattern = $pattern;
171
        return $new;
172
    }
173 4
174
    public function host(string $host): self
175 4
    {
176 4
        $route = clone $this;
177 4
        $route->host = rtrim($host, '/');
178
        return $route;
179
    }
180 2
181
    /**
182 2
     * Parameter default values indexed by parameter names
183 2
     *
184 2
     * @param array $defaults
185
     * @return self
186
     */
187
    public function defaults(array $defaults): self
188
    {
189
        $route = clone $this;
190
        $route->defaults = $defaults;
191
        return $route;
192
    }
193 1
194
    /**
195 1
     * @param callable|string|array $middleware
196 1
     */
197 1
    private function validateMiddleware($middleware): void
198
    {
199
        if (
200
            is_string($middleware) && is_subclass_of($middleware, MiddlewareInterface::class)
201
        ) {
202
            return;
203 21
        }
204
205
        if ($this->isCallable($middleware) && (!is_array($middleware) || !is_object($middleware[0]))) {
206 21
            return;
207
        }
208 1
209
        throw new InvalidArgumentException('Parameter should be either PSR middleware class name or a callable.');
210
    }
211 20
212 13
213
    /**
214
     * Prepends a handler that should be invoked for a matching route.
215 7
     * Last added handler will be invoked first.
216
     *
217
     * Parameter can be a PSR middleware class name, handler action
218
     * (an array of [handlerClass, handlerMethod]) or a callable.
219
     *
220
     * For handler action and callable typed parameters are automatically injected using dependency
221
     * injection container passed to the route. Current request and handler could be obtained by
222 12
     * type-hinting for ServerRequestInterface and RequestHandlerInterface.
223
     *
224 12
     * @param callable|string|array $middleware
225
     * @return Route
226
     */
227
    public function addMiddleware($middleware): self
228
    {
229
        $this->validateMiddleware($middleware);
230
231 12
        $route = clone $this;
232 3
        $route->middlewares[] = $middleware;
233
        return $route;
234
    }
235 3
236
    public function __toString(): string
237
    {
238 9
        $result = '';
239 9
240
        if ($this->name !== null) {
241
            $result .= '[' . $this->name . '] ';
242 9
        }
243
244
        if ($this->methods !== []) {
245
            $result .= implode(',', $this->methods) . ' ';
246
        }
247
        if ($this->host !== null && strrpos($this->pattern, $this->host) === false) {
248 20
            $result .= $this->host;
249
        }
250 20
        $result .= $this->pattern;
251 13
252
        return $result;
253
    }
254 7
255
    public function getName(): string
256
    {
257
        return $this->name ?? (implode(', ', $this->methods) . ' ' . $this->host . $this->pattern);
258
    }
259
260
    public function getMethods(): array
261
    {
262
        return $this->methods;
263
    }
264
265
    public function getPattern(): string
266
    {
267
        return $this->pattern;
268
    }
269
270
    public function getHost(): ?string
271 11
    {
272
        return $this->host;
273 11
    }
274
275 10
    public function getDefaults(): array
276 10
    {
277 10
        return $this->defaults;
278
    }
279
280 2
    private function isCallable($definition): bool
281
    {
282 2
        if (is_callable($definition)) {
283
            return true;
284 2
        }
285 1
286
        return is_array($definition) && array_keys($definition) === [0, 1] && in_array($definition[1], get_class_methods($definition[0]) ?? [], true);
287
    }
288
}
289