Passed
Pull Request — master (#68)
by Dmitriy
12:58
created

Route::getPattern()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
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 injectDispatcher(DispatcherInterface $dispatcher): void
33
    {
34
        $this->dispatcher = $dispatcher;
35
    }
36
37
    public function getDispatcherWithMiddlewares(): DispatcherInterface
38 44
    {
39
        if ($this->dispatcher->hasMiddlewares()) {
0 ignored issues
show
Bug introduced by
The method hasMiddlewares() 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

39
        if ($this->dispatcher->/** @scrutinizer ignore-call */ hasMiddlewares()) {

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