Passed
Push — master ( 0542bf...f1358e )
by Alexander
01:34 queued 14s
created

Route::prepareMiddleware()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 10
cc 3
nc 4
nop 1
crap 3
1
<?php
2
3
namespace Yiisoft\Router;
4
5
use InvalidArgumentException;
6
use LogicException;
7
use Yiisoft\Router\Middleware\Callback;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
use Psr\Http\Server\MiddlewareInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
13
/**
14
 * Route defines a mapping from URL to callback / name and vice versa
15
 */
16
final class Route implements MiddlewareInterface, RequestHandlerInterface
17
{
18
    private ?string $name = null;
19
    /** @var string[] */
20
    private array $methods;
21
    private string $pattern;
22
    private ?string $host = null;
23
    /**
24
     * @var MiddlewareInterface[]|callable[]
25
     */
26
    private array $middlewares = [];
27
    private array $parameters = [];
28
    private array $defaults = [];
29
    private RequestHandlerInterface $nextHandler;
30
31
    private function __construct()
32
    {
33
    }
34
35 17
    public static function get(string $pattern): self
36
    {
37 17
        $route = new static();
38 17
        $route->methods = [Method::GET];
39 17
        $route->pattern = $pattern;
40 17
        return $route;
41
    }
42
43 3
    public static function post(string $pattern): self
44
    {
45 3
        $route = new static();
46 3
        $route->methods = [Method::POST];
47 3
        $route->pattern = $pattern;
48 3
        return $route;
49
    }
50
51 1
    public static function put(string $pattern): self
52
    {
53 1
        $route = new static();
54 1
        $route->methods = [Method::PUT];
55 1
        $route->pattern = $pattern;
56 1
        return $route;
57
    }
58
59 1
    public static function delete(string $pattern): self
60
    {
61 1
        $route = new static();
62 1
        $route->methods = [Method::DELETE];
63 1
        $route->pattern = $pattern;
64 1
        return $route;
65
    }
66
67 1
    public static function patch(string $pattern): self
68
    {
69 1
        $route = new static();
70 1
        $route->methods = [Method::PATCH];
71 1
        $route->pattern = $pattern;
72 1
        return $route;
73
    }
74
75 1
    public static function head(string $pattern): self
76
    {
77 1
        $route = new static();
78 1
        $route->methods = [Method::HEAD];
79 1
        $route->pattern = $pattern;
80 1
        return $route;
81
    }
82
83 1
    public static function options(string $pattern): self
84
    {
85 1
        $route = new static();
86 1
        $route->methods = [Method::OPTIONS];
87 1
        $route->pattern = $pattern;
88 1
        return $route;
89
    }
90
91 2
    public static function methods(array $methods, string $pattern): self
92
    {
93 2
        $route = new static();
94 2
        $route->methods = $methods;
95 2
        $route->pattern = $pattern;
96 2
        return $route;
97
    }
98
99 1
    public static function anyMethod(string $pattern): self
100
    {
101 1
        $route = new static();
102 1
        $route->methods = Method::ANY;
103 1
        $route->pattern = $pattern;
104 1
        return $route;
105
    }
106
107 2
    public function name(string $name): self
108
    {
109 2
        $route = clone $this;
110 2
        $route->name = $name;
111 2
        return $route;
112
    }
113
114 1
    public function pattern(string $pattern): self
115
    {
116 1
        $new = clone $this;
117 1
        $new->pattern = $pattern;
118 1
        return $new;
119
    }
120
121 2
    public function host(string $host): self
122
    {
123 2
        $route = clone $this;
124 2
        $route->host = rtrim($host, '/');
125 2
        return $route;
126
    }
127
128
    /**
129
     * Parameter validation rules indexed by parameter names
130
     *
131
     * @param array $parameters
132
     * @return Route
133
     */
134 1
    public function parameters(array $parameters): self
135
    {
136 1
        $route = clone $this;
137 1
        $route->parameters = $parameters;
138 1
        return $route;
139
    }
140
141
    /**
142
     * Parameter default values indexed by parameter names
143
     *
144
     * @param array $defaults
145
     * @return Route
146
     */
147 1
    public function defaults(array $defaults): self
148
    {
149 1
        $route = clone $this;
150 1
        $route->defaults = $defaults;
151 1
        return $route;
152
    }
153
154
    /**
155
     * @param callable|MiddlewareInterface $middleware
156
     * @return MiddlewareInterface
157
     */
158 7
    private function prepareMiddleware($middleware): MiddlewareInterface
159
    {
160 7
        if (is_callable($middleware)) {
161 1
            $middleware = new Callback($middleware);
0 ignored issues
show
Bug introduced by
It seems like $middleware can also be of type Psr\Http\Server\MiddlewareInterface; however, parameter $callback of Yiisoft\Router\Middleware\Callback::__construct() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

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

161
            $middleware = new Callback(/** @scrutinizer ignore-type */ $middleware);
Loading history...
162
        }
163
164 7
        if (!$middleware instanceof MiddlewareInterface) {
165 1
            throw new InvalidArgumentException('Parameter should be either a PSR middleware or a callable.');
166
        }
167
168 6
        return $middleware;
169
    }
170
171
    /**
172
     * Adds a handler that should be invoked for a matching route.
173
     * It can be either a PSR middleware or a callable with the following signature:
174
     *
175
     * ```
176
     * function (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
177
     * ```
178
     *
179
     * @param MiddlewareInterface|callable $middleware
180
     * @return Route
181
     */
182 7
    public function to($middleware): self
183
    {
184 7
        $route = clone $this;
185 7
        $route->middlewares[] = $this->prepareMiddleware($middleware);
186 6
        return $route;
187
    }
188
189
    /**
190
     * Adds a handler that should be invoked for a matching route.
191
     * It can be either a PSR middleware or a callable with the following signature:
192
     *
193
     * ```
194
     * function (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
195
     * ```
196
     *
197
     * @param MiddlewareInterface|callable $middleware
198
     * @return Route
199
     */
200 1
    public function then($middleware): self
201
    {
202 1
        return $this->to($middleware);
203
    }
204
205
    /**
206
     * Prepends a handler that should be invoked for a matching route.
207
     * It can be either a PSR middleware or a callable with the following signature:
208
     *
209
     * ```
210
     * function (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
211
     * ```
212
     *
213
     * @param MiddlewareInterface|callable $middleware
214
     * @return Route
215
     */
216 1
    public function prepend($middleware): self
217
    {
218 1
        $route = clone $this;
219 1
        array_unshift($route->middlewares, $this->prepareMiddleware($middleware));
220 1
        return $route;
221
    }
222
223 2
    public function __toString()
224
    {
225 2
        $result = '';
226
227 2
        if ($this->name !== null) {
228 1
            $result .= '[' . $this->name . '] ';
229
        }
230
231 2
        if ($this->methods !== []) {
232 2
            $result .= implode(',', $this->methods) . ' ';
233
        }
234 2
        if ($this->host !== null && strrpos($this->pattern, $this->host) === false) {
235 1
            $result .= $this->host;
236
        }
237 2
        $result .= $this->pattern;
238
239 2
        return $result;
240
    }
241
242 2
    public function getName(): string
243
    {
244 2
        return $this->name ?? (implode(', ', $this->methods) . ' ' . $this->pattern);
245
    }
246
247 9
    public function getMethods(): array
248
    {
249 9
        return $this->methods;
250
    }
251
252 1
    public function getPattern(): string
253
    {
254 1
        return $this->pattern;
255
    }
256
257 1
    public function getHost(): ?string
258
    {
259 1
        return $this->host;
260
    }
261
262 1
    public function getParameters(): array
263
    {
264 1
        return $this->parameters;
265
    }
266
267 1
    public function getDefaults(): array
268
    {
269 1
        return $this->defaults;
270
    }
271
272
    /**
273
     * @internal please use {@see dispatch()} or {@see process()}
274
     * @param ServerRequestInterface $request
275
     * @return ResponseInterface
276
     */
277 6
    public function handle(ServerRequestInterface $request): ResponseInterface
278
    {
279 6
        $middleware = current($this->middlewares);
280 6
        next($this->middlewares);
281 6
        if ($middleware === false) {
282
            if (!$this->nextHandler !== null) {
0 ignored issues
show
introduced by
The condition ! $this->nextHandler !== null is always true.
Loading history...
283
                return $this->nextHandler->handle($request);
284
            }
285
286
            throw new LogicException('Middleware stack exhausted');
287
        }
288
289 6
        return $middleware->process($request, $this);
290
    }
291
292 6
    public function dispatch(ServerRequestInterface $request): ResponseInterface
293
    {
294 6
        reset($this->middlewares);
295 6
        return $this->handle($request);
296
    }
297
298 6
    public function process(ServerRequestInterface $request, RequestHandlerInterface $nextHandler): ResponseInterface
299
    {
300 6
        $this->nextHandler = $nextHandler;
301 6
        return $this->dispatch($request);
302
    }
303
}
304