Passed
Push — master ( 138cac...b16689 )
by Alexander
02:23 queued 39s
created

Route.php$0 ➔ wrap()   A

Complexity

Conditions 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
ccs 5
cts 5
cp 1
rs 9.7666
cc 1
crap 1

2 Methods

Rating   Name   Duplication   Size   Complexity  
A Route.php$0 ➔ __construct() 0 4 1
A Route.php$0 ➔ handle() 0 3 1
1
<?php
2
3
namespace Yiisoft\Router;
4
5
use InvalidArgumentException;
6
use Yiisoft\Http\Method;
7
use Yiisoft\Router\Middleware\Callback;
8
use Psr\Http\Server\MiddlewareInterface;
9
use Psr\Http\Server\RequestHandlerInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Message\ResponseInterface;
12
13
/**
14
 * Route defines a mapping from URL to callback / name and vice versa
15
 */
16
final class Route implements MiddlewareInterface
17
{
18
    private ?string $name = null;
19
    /** @var string[] */
20
    private array $methods;
21
    private string $pattern;
22
    private ?string $host = null;
23
    /**
24
     * Contains a chain of middleware wrapped in handlers.
25
     * Each handler points to the handler of middleware that will be processed next.
26
     * @var RequestHandlerInterface|null stack of middleware
27
     */
28
    private ?RequestHandlerInterface $stack = null;
29
30
    /**
31
     * @var MiddlewareInterface[]|callable[]
32
     */
33
    private array $middlewares = [];
34
    private array $defaults = [];
35
36
    private function __construct()
37
    {
38
    }
39
40
    /**
41
     * @param string $pattern
42
     * @param callable|MiddlewareInterface|null $middleware
43
     * @return static
44
     */
45
    public static function get(string $pattern, $middleware = null): self
46
    {
47
        return static::methods([Method::GET], $pattern, $middleware);
48
    }
49
50
    /**
51
     * @param string $pattern
52
     * @param callable|MiddlewareInterface|null $middleware
53
     * @return static
54
     */
55
    public static function post(string $pattern, $middleware = null): self
56
    {
57
        return static::methods([Method::POST], $pattern, $middleware);
58
    }
59
60
    /**
61
     * @param string $pattern
62
     * @param callable|MiddlewareInterface|null $middleware
63
     * @return static
64
     */
65
    public static function put(string $pattern, $middleware = null): self
66
    {
67
        return static::methods([Method::PUT], $pattern, $middleware);
68
    }
69
70
    /**
71
     * @param string $pattern
72
     * @param callable|MiddlewareInterface|null $middleware
73
     * @return static
74
     */
75
    public static function delete(string $pattern, $middleware = null): self
76
    {
77
        return static::methods([Method::DELETE], $pattern, $middleware);
78
    }
79
80
    /**
81
     * @param string $pattern
82
     * @param callable|MiddlewareInterface|null $middleware
83
     * @return static
84
     */
85
    public static function patch(string $pattern, $middleware = null): self
86
    {
87
        return static::methods([Method::PATCH], $pattern, $middleware);
88
    }
89
90
    /**
91
     * @param string $pattern
92
     * @param callable|MiddlewareInterface|null $middleware
93
     * @return static
94
     */
95
    public static function head(string $pattern, $middleware = null): self
96
    {
97
        return static::methods([Method::HEAD], $pattern, $middleware);
98
    }
99
100
    /**
101
     * @param string $pattern
102
     * @param callable|MiddlewareInterface|null $middleware
103
     * @return static
104
     */
105
    public static function options(string $pattern, $middleware = null): self
106
    {
107
        return static::methods([Method::OPTIONS], $pattern, $middleware);
108
    }
109
110
    /**
111
     * @param array $methods
112
     * @param string $pattern
113
     * @param callable|MiddlewareInterface|null $middleware
114
     * @return static
115
     */
116
    public static function methods(array $methods, string $pattern, $middleware = null): self
117
    {
118
        $route = new static();
119
        $route->methods = $methods;
120
        $route->pattern = $pattern;
121
        if ($middleware !== null) {
122
            $route->middlewares[] = $route->prepareMiddleware($middleware);
123
        }
124
        return $route;
125
    }
126
127
    /**
128
     * @param string $pattern
129
     * @param callable|MiddlewareInterface|null $middleware
130
     * @return static
131
     */
132
    public static function anyMethod(string $pattern, $middleware = null): self
133
    {
134
        return static::methods(Method::ANY, $pattern, $middleware);
135
    }
136
137
    public function name(string $name): self
138
    {
139
        $route = clone $this;
140
        $route->name = $name;
141
        return $route;
142
    }
143
144
    public function pattern(string $pattern): self
145
    {
146
        $new = clone $this;
147
        $new->pattern = $pattern;
148
        return $new;
149
    }
150
151
    public function host(string $host): self
152
    {
153
        $route = clone $this;
154
        $route->host = rtrim($host, '/');
155
        return $route;
156
    }
157
158
    /**
159
     * Parameter default values indexed by parameter names
160
     *
161
     * @param array $defaults
162
     * @return self
163
     */
164
    public function defaults(array $defaults): self
165
    {
166
        $route = clone $this;
167
        $route->defaults = $defaults;
168
        return $route;
169
    }
170
171
    /**
172
     * @param callable|MiddlewareInterface $middleware
173
     * @return MiddlewareInterface
174
     */
175
    private function prepareMiddleware($middleware): MiddlewareInterface
176
    {
177
        if (is_callable($middleware)) {
178
            $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

178
            $middleware = new Callback(/** @scrutinizer ignore-type */ $middleware);
Loading history...
179
        }
180
181
        if (!$middleware instanceof MiddlewareInterface) {
182
            throw new InvalidArgumentException('Parameter should be either a PSR middleware or a callable.');
183
        }
184
185
        return $middleware;
186
    }
187
188
    /**
189
     * Prepends a handler that should be invoked for a matching route.
190
     * Last added handler will be invoked first.
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
     *     * Last added middleware will be invoked first.
197
     *
198
     * @param MiddlewareInterface|callable $middleware
199
     * @return Route
200
     */
201
    public function addMiddleware($middleware): self
202
    {
203
        $route = clone $this;
204
        array_unshift($route->middlewares, $this->prepareMiddleware($middleware));
205
        return $route;
206
    }
207
208
    public function __toString()
209
    {
210
        $result = '';
211
212
        if ($this->name !== null) {
213
            $result .= '[' . $this->name . '] ';
214
        }
215
216
        if ($this->methods !== []) {
217
            $result .= implode(',', $this->methods) . ' ';
218
        }
219
        if ($this->host !== null && strrpos($this->pattern, $this->host) === false) {
220
            $result .= $this->host;
221
        }
222
        $result .= $this->pattern;
223
224
        return $result;
225
    }
226
227
    public function getName(): string
228
    {
229
        return $this->name ?? (implode(', ', $this->methods) . ' ' . $this->pattern);
230
    }
231
232
    public function getMethods(): array
233
    {
234
        return $this->methods;
235
    }
236
237
    public function getPattern(): string
238
    {
239
        return $this->pattern;
240
    }
241
242
    public function getHost(): ?string
243
    {
244
        return $this->host;
245
    }
246
247
    public function getDefaults(): array
248
    {
249
        return $this->defaults;
250
    }
251
252
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
253
    {
254
        if ($this->stack === null) {
255
            for ($i = count($this->middlewares) - 1; $i >= 0; $i--) {
256
                $handler = $this->wrap($this->middlewares[$i], $handler);
257
            }
258
            $this->stack = $handler;
259
        }
260
261
        return $this->stack->handle($request);
0 ignored issues
show
Bug introduced by
The method handle() 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

261
        return $this->stack->/** @scrutinizer ignore-call */ handle($request);

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...
262
    }
263
264
    /**
265
     * Wraps handler by middlewares
266
     */
267 6
    private function wrap(MiddlewareInterface $middleware, RequestHandlerInterface $handler): RequestHandlerInterface
268
    {
269
        return new class($middleware, $handler) implements RequestHandlerInterface {
270
            private MiddlewareInterface $middleware;
271
            private RequestHandlerInterface $handler;
272
273
            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $handler)
274
            {
275 6
                $this->middleware = $middleware;
276 6
                $this->handler = $handler;
277
            }
278
279 6
            public function handle(ServerRequestInterface $request): ResponseInterface
280
            {
281 6
                return $this->middleware->process($request, $this->handler);
282
            }
283
        };
284
    }
285
}
286