Passed
Push — master ( f908b9...b51215 )
by Alexander
01:21
created

Route::dispatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
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 $defaults = [];
28
    private RequestHandlerInterface $nextHandler;
29
30
    private function __construct()
31
    {
32
    }
33
34 1
    public static function get(string $pattern): self
35
    {
36 1
        $route = new static();
37 1
        $route->methods = [Method::GET];
0 ignored issues
show
Bug introduced by
The type Yiisoft\Router\Method was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
38
        $route->pattern = $pattern;
39
        return $route;
40
    }
41
42
    public static function post(string $pattern): self
43
    {
44
        $route = new static();
45
        $route->methods = [Method::POST];
46
        $route->pattern = $pattern;
47
        return $route;
48
    }
49
50
    public static function put(string $pattern): self
51
    {
52
        $route = new static();
53
        $route->methods = [Method::PUT];
54
        $route->pattern = $pattern;
55
        return $route;
56
    }
57
58
    public static function delete(string $pattern): self
59
    {
60
        $route = new static();
61
        $route->methods = [Method::DELETE];
62
        $route->pattern = $pattern;
63
        return $route;
64
    }
65
66
    public static function patch(string $pattern): self
67
    {
68
        $route = new static();
69
        $route->methods = [Method::PATCH];
70
        $route->pattern = $pattern;
71
        return $route;
72
    }
73
74
    public static function head(string $pattern): self
75
    {
76
        $route = new static();
77
        $route->methods = [Method::HEAD];
78
        $route->pattern = $pattern;
79
        return $route;
80
    }
81
82
    public static function options(string $pattern): self
83
    {
84
        $route = new static();
85
        $route->methods = [Method::OPTIONS];
86
        $route->pattern = $pattern;
87
        return $route;
88
    }
89
90 2
    public static function methods(array $methods, string $pattern): self
91
    {
92 2
        $route = new static();
93 2
        $route->methods = $methods;
94 2
        $route->pattern = $pattern;
95 2
        return $route;
96
    }
97
98
    public static function anyMethod(string $pattern): self
99
    {
100
        $route = new static();
101
        $route->methods = Method::ANY;
102
        $route->pattern = $pattern;
103
        return $route;
104
    }
105
106 1
    public function name(string $name): self
107
    {
108 1
        $route = clone $this;
109 1
        $route->name = $name;
110 1
        return $route;
111
    }
112
113
    public function pattern(string $pattern): self
114
    {
115
        $new = clone $this;
116
        $new->pattern = $pattern;
117
        return $new;
118
    }
119
120 1
    public function host(string $host): self
121
    {
122 1
        $route = clone $this;
123 1
        $route->host = rtrim($host, '/');
124 1
        return $route;
125
    }
126
127
    /**
128
     * Parameter default values indexed by parameter names
129
     *
130
     * @param array $defaults
131
     * @return Route
132
     */
133
    public function defaults(array $defaults): self
134
    {
135
        $route = clone $this;
136
        $route->defaults = $defaults;
137
        return $route;
138
    }
139
140
    /**
141
     * @param callable|MiddlewareInterface $middleware
142
     * @return MiddlewareInterface
143
     */
144
    private function prepareMiddleware($middleware): MiddlewareInterface
145
    {
146
        if (is_callable($middleware)) {
147
            $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

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