Passed
Push — master ( b51215...92fe06 )
by Alexander
01:26
created

Route::handle()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.4746

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 13
ccs 5
cts 8
cp 0.625
rs 10
cc 3
nc 3
nop 1
crap 3.4746
1
<?php
2
3
namespace Yiisoft\Router;
4
5
use InvalidArgumentException;
6
use LogicException;
7
use Yiisoft\Http\Method;
8
use Yiisoft\Router\Middleware\Callback;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Server\MiddlewareInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
14
/**
15
 * Route defines a mapping from URL to callback / name and vice versa
16
 */
17
final class Route implements MiddlewareInterface, RequestHandlerInterface
18
{
19
    private ?string $name = null;
20
    /** @var string[] */
21
    private array $methods;
22
    private string $pattern;
23
    private ?string $host = null;
24
    /**
25
     * @var MiddlewareInterface[]|callable[]
26
     */
27
    private array $middlewares = [];
28
    private array $defaults = [];
29
    private RequestHandlerInterface $nextHandler;
30
31
    private function __construct()
32
    {
33
    }
34
35 16
    public static function get(string $pattern): self
36
    {
37 16
        $route = new static();
38 16
        $route->methods = [Method::GET];
39 16
        $route->pattern = $pattern;
40 16
        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 default values indexed by parameter names
130
     *
131
     * @param array $defaults
132
     * @return Route
133
     */
134 1
    public function defaults(array $defaults): self
135
    {
136 1
        $route = clone $this;
137 1
        $route->defaults = $defaults;
138 1
        return $route;
139
    }
140
141
    /**
142
     * @param callable|MiddlewareInterface $middleware
143
     * @return MiddlewareInterface
144
     */
145 7
    private function prepareMiddleware($middleware): MiddlewareInterface
146
    {
147 7
        if (is_callable($middleware)) {
148 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

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