1 | <?php |
||||
2 | declare(strict_types=1); |
||||
3 | |||||
4 | namespace Lead\Router; |
||||
5 | |||||
6 | use ArrayAccess; |
||||
7 | use Closure; |
||||
8 | use Countable; |
||||
9 | use Iterator; |
||||
10 | use Lead\Router\Exception\ParserException; |
||||
11 | use Lead\Router\Exception\RouteNotFoundException; |
||||
12 | use Lead\Router\Exception\RouterException; |
||||
13 | use Psr\Http\Message\RequestInterface; |
||||
14 | use Psr\Http\Message\ServerRequestInterface; |
||||
15 | use RuntimeException; |
||||
16 | |||||
17 | /** |
||||
18 | * The Router class. |
||||
19 | */ |
||||
20 | class Router implements ArrayAccess, Iterator, Countable, RouterInterface |
||||
21 | { |
||||
22 | /** |
||||
23 | * @var bool |
||||
24 | */ |
||||
25 | protected $_skipNext; |
||||
26 | |||||
27 | /** |
||||
28 | * @var array |
||||
29 | */ |
||||
30 | protected $_data = []; |
||||
31 | |||||
32 | /** |
||||
33 | * @var array |
||||
34 | */ |
||||
35 | protected $_pattern = []; |
||||
36 | |||||
37 | /** |
||||
38 | * Class dependencies. |
||||
39 | * |
||||
40 | * @var array |
||||
41 | */ |
||||
42 | protected $_classes = []; |
||||
43 | |||||
44 | /** |
||||
45 | * Hosts. |
||||
46 | * |
||||
47 | * @var array |
||||
48 | */ |
||||
49 | protected $_hosts = []; |
||||
50 | |||||
51 | /** |
||||
52 | * Routes. |
||||
53 | * |
||||
54 | * @var array |
||||
55 | */ |
||||
56 | protected $_routes = []; |
||||
57 | |||||
58 | /** |
||||
59 | * Scopes stack. |
||||
60 | * |
||||
61 | * @var array |
||||
62 | */ |
||||
63 | protected $_scopes = []; |
||||
64 | |||||
65 | /** |
||||
66 | * Base path. |
||||
67 | * |
||||
68 | * @param string |
||||
69 | */ |
||||
70 | protected $_basePath = ''; |
||||
71 | |||||
72 | /** |
||||
73 | * Dispatching strategies. |
||||
74 | * |
||||
75 | * @param array |
||||
76 | */ |
||||
77 | protected $_strategies = []; |
||||
78 | |||||
79 | /** |
||||
80 | * Defaults parameters to use when generating URLs in a dispatching context. |
||||
81 | * |
||||
82 | * @var array |
||||
83 | */ |
||||
84 | protected $_defaults = []; |
||||
85 | |||||
86 | /** |
||||
87 | * Default handler |
||||
88 | * |
||||
89 | * @var callable|null |
||||
90 | */ |
||||
91 | protected $defaultHandler = null; |
||||
92 | |||||
93 | /** |
||||
94 | * Constructor |
||||
95 | * |
||||
96 | * @param array $config |
||||
97 | */ |
||||
98 | public function __construct($config = []) |
||||
99 | { |
||||
100 | $defaults = [ |
||||
101 | 'basePath' => '', |
||||
102 | 'scope' => [], |
||||
103 | 'strategies' => [], |
||||
104 | 'defaultHandler' => null, |
||||
105 | 'classes' => [ |
||||
106 | 'parser' => 'Lead\Router\Parser', |
||||
107 | 'host' => 'Lead\Router\Host', |
||||
108 | 'route' => 'Lead\Router\Route', |
||||
109 | 'scope' => 'Lead\Router\Scope' |
||||
110 | ] |
||||
111 | 55 | ]; |
|||
112 | |||||
113 | 55 | $config += $defaults; |
|||
114 | 55 | $this->_classes = $config['classes']; |
|||
115 | 55 | $this->_strategies = $config['strategies']; |
|||
116 | 55 | $this->setDefaultHandler($config['defaultHandler']); |
|||
117 | 55 | $this->setBasePath($config['basePath']); |
|||
118 | |||||
119 | 55 | $scope = $this->_classes['scope']; |
|||
120 | 55 | $this->_scopes[] = new $scope(['router' => $this]); |
|||
121 | } |
||||
122 | |||||
123 | /** |
||||
124 | * Sets the default handler for routes |
||||
125 | * |
||||
126 | * @param mixed $handler |
||||
127 | * @return $this |
||||
128 | */ |
||||
129 | public function setDefaultHandler($handler): RouterInterface |
||||
130 | { |
||||
131 | 55 | $this->_defaultHandler = $handler; |
|||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
132 | |||||
133 | 55 | return $this; |
|||
134 | } |
||||
135 | |||||
136 | /** |
||||
137 | * Returns the current router scope. |
||||
138 | * |
||||
139 | * @return \Lead\Router\ScopeInterface The current scope instance. |
||||
140 | */ |
||||
141 | public function scope(): ScopeInterface |
||||
142 | { |
||||
143 | 16 | return end($this->_scopes); |
|||
144 | } |
||||
145 | |||||
146 | /** |
||||
147 | * Pushes a new router scope context. |
||||
148 | * |
||||
149 | * @param object $scope A scope instance. |
||||
150 | * @return self |
||||
151 | */ |
||||
152 | public function pushScope($scope): RouterInterface |
||||
153 | { |
||||
154 | 16 | $this->_scopes[] = $scope; |
|||
155 | |||||
156 | 16 | return $this; |
|||
157 | } |
||||
158 | |||||
159 | /** |
||||
160 | * Pops the current router scope context. |
||||
161 | * |
||||
162 | * @return \Lead\Router\ScopeInterface The popped scope instance. |
||||
163 | */ |
||||
164 | public function popScope(): ScopeInterface |
||||
165 | { |
||||
166 | 16 | return array_pop($this->_scopes); |
|||
167 | } |
||||
168 | |||||
169 | /** |
||||
170 | * Gets the base path |
||||
171 | * |
||||
172 | * @param string $basePath The base path to set or none to get the setted one. |
||||
173 | * @return string |
||||
174 | */ |
||||
175 | public function getBasePath(): string |
||||
176 | { |
||||
177 | 8 | return $this->_basePath; |
|||
178 | } |
||||
179 | |||||
180 | /** |
||||
181 | * Sets the base path |
||||
182 | * |
||||
183 | * @param string $basePath Base Path |
||||
184 | * @return $this |
||||
185 | */ |
||||
186 | public function setBasePath(string $basePath): self |
||||
187 | { |
||||
188 | 55 | $basePath = trim($basePath, '/'); |
|||
189 | 55 | $this->_basePath = $basePath ? '/' . $basePath : ''; |
|||
190 | |||||
191 | 55 | return $this; |
|||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * Gets/sets the base path of the router. |
||||
196 | * |
||||
197 | * @deprecated Use setBasePath() and getBasePath() instead |
||||
198 | * @param string|null $basePath The base path to set or none to get the setted one. |
||||
199 | * @return string|self |
||||
200 | */ |
||||
201 | public function basePath(?string $basePath = null) |
||||
202 | { |
||||
203 | if ($basePath === null) { |
||||
204 | return $this->_basePath; |
||||
205 | } |
||||
206 | |||||
207 | 1 | return $this->setBasePath($basePath); |
|||
208 | } |
||||
209 | |||||
210 | /** |
||||
211 | * Adds a route to the router |
||||
212 | * |
||||
213 | * @param \Lead\Router\RouteInterface $route Route object |
||||
214 | * @return \Lead\Router\RouterInterface |
||||
215 | */ |
||||
216 | public function addRoute(RouteInterface $route): RouterInterface { |
||||
217 | $options['pattern'] = $pattern = $route->getPattern(); |
||||
0 ignored issues
–
show
The method
getPattern() does not exist on Lead\Router\RouteInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Lead\Router\RouteInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() Comprehensibility
Best Practice
introduced
by
|
|||||
218 | $options['handler'] = $route->getHandler(); |
||||
219 | $options['scope'] = $route->getScope(); |
||||
220 | |||||
221 | $scheme = $options['scheme']; |
||||
222 | $host = $options['host']; |
||||
223 | |||||
224 | if (isset($this->_hosts[$scheme][$host])) { |
||||
225 | $options['host'] = $this->_hosts[$scheme][$host]; |
||||
226 | } |
||||
227 | |||||
228 | $patternKey = md5($options['pattern'] . '-' . $options['name']); |
||||
229 | |||||
230 | if (isset($this->_pattern[$scheme][$host][$patternKey])) { |
||||
231 | $route = $this->_pattern[$scheme][$host][$patternKey]; |
||||
232 | } else { |
||||
233 | $this->_hosts[$scheme][$host] = $route->getHost(); |
||||
234 | } |
||||
235 | |||||
236 | if (!isset($this->_pattern[$scheme][$host][$patternKey])) { |
||||
237 | $this->_pattern[$scheme][$host][$patternKey] = $route; |
||||
238 | } |
||||
239 | |||||
240 | $methods = $route->getMethods(); |
||||
241 | foreach ($methods as $method) { |
||||
242 | $this->_routes[$scheme][$host][strtoupper($method)][] = $route; |
||||
243 | } |
||||
244 | |||||
245 | $this->_data[$route->getName()] = $route; |
||||
246 | |||||
247 | return $this; |
||||
248 | } |
||||
249 | |||||
250 | /** |
||||
251 | * Adds a route. |
||||
252 | * |
||||
253 | * @param string|array $pattern The route's pattern. |
||||
254 | * @param Closure|array $options An array of options or the callback handler. |
||||
255 | * @param Closure|null $handler The callback handler. |
||||
256 | * @return self |
||||
257 | */ |
||||
258 | public function bind($pattern, $options = [], $handler = null): RouteInterface |
||||
259 | { |
||||
260 | if (!is_array($options)) { |
||||
261 | 18 | $handler = $options; |
|||
262 | 18 | $options = []; |
|||
263 | } |
||||
264 | |||||
265 | if (empty($handler) && !empty($this->_defaultHandler)) { |
||||
266 | $handler = $this->_defaultHandler; |
||||
267 | } |
||||
268 | |||||
269 | if ($handler !== null) { |
||||
270 | if (!$handler instanceof Closure && !method_exists($handler, '__invoke')) { |
||||
271 | 1 | throw new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method."); |
|||
272 | } |
||||
273 | } |
||||
274 | |||||
275 | if (isset($options['method'])) { |
||||
276 | 1 | throw new RouterException("Use the `'methods'` option to limit HTTP verbs on a route binding definition."); |
|||
277 | } |
||||
278 | |||||
279 | 45 | $scope = end($this->_scopes); |
|||
280 | 45 | $options = $scope->scopify($options); |
|||
281 | 45 | $options['pattern'] = $pattern; |
|||
282 | 45 | $options['handler'] = $handler; |
|||
283 | 45 | $options['scope'] = $scope; |
|||
284 | |||||
285 | 45 | $scheme = $options['scheme']; |
|||
286 | 45 | $host = $options['host']; |
|||
287 | |||||
288 | if (isset($this->_hosts[$scheme][$host])) { |
||||
289 | $options['host'] = $this->_hosts[$scheme][$host]; |
||||
290 | } |
||||
291 | |||||
292 | 45 | $patternKey = md5($options['pattern'] . '-' . $options['name']); |
|||
293 | |||||
294 | if (isset($this->_pattern[$scheme][$host][$patternKey])) { |
||||
295 | 2 | $instance = $this->_pattern[$scheme][$host][$patternKey]; |
|||
296 | } else { |
||||
297 | 45 | $route = $this->_classes['route']; |
|||
298 | 45 | $instance = new $route($options); |
|||
299 | 45 | $this->_hosts[$scheme][$host] = $instance->getHost(); |
|||
300 | } |
||||
301 | |||||
302 | if (!isset($this->_pattern[$scheme][$host][$patternKey])) { |
||||
303 | 45 | $this->_pattern[$scheme][$host][$patternKey] = $instance; |
|||
304 | } |
||||
305 | |||||
306 | 45 | $methods = $options['methods'] ? (array)$options['methods'] : []; |
|||
307 | |||||
308 | 45 | $instance->allow($methods); |
|||
309 | |||||
310 | foreach ($methods as $method) { |
||||
311 | 45 | $this->_routes[$scheme][$host][strtoupper($method)][] = $instance; |
|||
312 | } |
||||
313 | |||||
314 | if (isset($options['name'])) { |
||||
315 | 45 | $this->_data[$options['name']] = $instance; |
|||
316 | } |
||||
317 | |||||
318 | 45 | return $instance; |
|||
319 | } |
||||
320 | |||||
321 | /** |
||||
322 | * Groups some routes inside a new scope. |
||||
323 | * |
||||
324 | * @param string|array $prefix The group's prefix pattern or the options array. |
||||
325 | * @param Closure|array $options An array of options or the callback handler. |
||||
326 | * @param Closure|null $handler The callback handler. |
||||
327 | * @return \Lead\Router\ScopeInterface The newly created scope instance. |
||||
328 | */ |
||||
329 | public function group($prefix, $options, $handler = null) |
||||
330 | { |
||||
331 | if (!is_array($options)) { |
||||
332 | 11 | $handler = $options; |
|||
333 | if (is_string($prefix)) { |
||||
334 | 10 | $options = []; |
|||
335 | } else { |
||||
336 | 1 | $options = $prefix; |
|||
337 | 1 | $prefix = ''; |
|||
338 | } |
||||
339 | } |
||||
340 | if (!$handler instanceof Closure && !method_exists($handler, '__invoke')) { |
||||
341 | 1 | throw new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method."); |
|||
342 | } |
||||
343 | |||||
344 | 16 | $options['prefix'] = isset($options['prefix']) ? $options['prefix'] : $prefix; |
|||
345 | |||||
346 | 16 | $scope = $this->scope(); |
|||
347 | |||||
348 | 16 | $this->pushScope($scope->seed($options)); |
|||
349 | |||||
350 | 16 | $handler($this); |
|||
351 | |||||
352 | 16 | return $this->popScope(); |
|||
353 | } |
||||
354 | |||||
355 | /** |
||||
356 | * Gets information required for routing from a server request |
||||
357 | * |
||||
358 | * @param \Psr\Http\Message\ServerRequestInterface $request Server Request |
||||
359 | * @return array |
||||
360 | */ |
||||
361 | protected function _getRequestInformation(ServerRequestInterface $request): array |
||||
362 | { |
||||
363 | 1 | $uri = $request->getUri(); |
|||
364 | |||||
365 | if (method_exists($request, 'basePath')) { |
||||
366 | $this->setBasePath($request->basePath()); |
||||
367 | } |
||||
368 | |||||
369 | return [ |
||||
370 | 'scheme' => $uri->getScheme(), |
||||
371 | 'host' => $uri->getHost(), |
||||
372 | 'method' => $request->getMethod(), |
||||
373 | 'path' => $uri->getPath() |
||||
374 | 1 | ]; |
|||
375 | } |
||||
376 | |||||
377 | /** |
||||
378 | * Routes a Request. |
||||
379 | * |
||||
380 | * @param mixed $request The request to route. |
||||
381 | * @return \Lead\Router\RouteInterface A route matching the request or a "route not found" route. |
||||
382 | */ |
||||
383 | public function route($request): RouteInterface |
||||
384 | { |
||||
385 | $defaults = [ |
||||
386 | 'path' => '', |
||||
387 | 'method' => 'GET', |
||||
388 | 'host' => '*', |
||||
389 | 'scheme' => '*' |
||||
390 | 40 | ]; |
|||
391 | |||||
392 | 40 | $this->_defaults = []; |
|||
393 | |||||
394 | if ($request instanceof ServerRequestInterface) { |
||||
395 | 1 | $r = $this->_getRequestInformation($request); |
|||
396 | } elseif (!is_array($request)) { |
||||
397 | 38 | $r = array_combine(array_keys($defaults), func_get_args() + array_values($defaults)); |
|||
398 | } else { |
||||
399 | 1 | $r = $request + $defaults; |
|||
400 | } |
||||
401 | |||||
402 | 40 | $r = $this->_normalizeRequest($r); |
|||
403 | |||||
404 | 40 | $route = $this->_route($r); |
|||
405 | if ($route instanceof RouteInterface) { |
||||
406 | 35 | $route->request = is_object($request) ? $request : $r; |
|||
0 ignored issues
–
show
|
|||||
407 | foreach ($route->getPersistentParams() as $key) { |
||||
0 ignored issues
–
show
The method
getPersistentParams() does not exist on Lead\Router\RouteInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Lead\Router\RouteInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
408 | if (isset($route->params[$key])) { |
||||
0 ignored issues
–
show
|
|||||
409 | 2 | $this->_defaults[$key] = $route->params[$key]; |
|||
410 | } |
||||
411 | } |
||||
412 | |||||
413 | 35 | return $route; |
|||
0 ignored issues
–
show
|
|||||
414 | } |
||||
415 | |||||
416 | 13 | $message = "No route found for `{$r['scheme']}:{$r['host']}:{$r['method']}:/{$r['path']}`."; |
|||
417 | 13 | throw new RouteNotFoundException($message); |
|||
418 | } |
||||
419 | |||||
420 | /** |
||||
421 | * Normalizes a request. |
||||
422 | * |
||||
423 | * @param array $request The request to normalize. |
||||
424 | * @return array The normalized request. |
||||
425 | */ |
||||
426 | protected function _normalizeRequest(array $request): array |
||||
427 | { |
||||
428 | if (preg_match('~^(?:[a-z]+:)?//~i', $request['path'])) { |
||||
429 | 5 | $parsed = array_intersect_key(parse_url($request['path']), $request); |
|||
430 | 5 | $request = $parsed + $request; |
|||
431 | } |
||||
432 | |||||
433 | 40 | $request['path'] = (ltrim((string)strtok($request['path'], '?'), '/')); |
|||
434 | 40 | $request['method'] = strtoupper($request['method']); |
|||
435 | |||||
436 | 40 | return $request; |
|||
437 | } |
||||
438 | |||||
439 | /** |
||||
440 | * Routes a request. |
||||
441 | * |
||||
442 | * @param array $request The request to route. |
||||
443 | * @return null|\Lead\Router\RouteInterface |
||||
444 | */ |
||||
445 | protected function _route($request): ?RouteInterface |
||||
446 | { |
||||
447 | 40 | $path = $request['path']; |
|||
0 ignored issues
–
show
|
|||||
448 | 40 | $httpMethod = $request['method']; |
|||
449 | 40 | $host = $request['host']; |
|||
0 ignored issues
–
show
|
|||||
450 | 40 | $scheme = $request['scheme']; |
|||
451 | |||||
452 | 40 | $allowedSchemes = array_unique([$scheme => $scheme, '*' => '*']); |
|||
453 | 40 | $allowedMethods = array_unique([$httpMethod => $httpMethod, '*' => '*']); |
|||
454 | |||||
455 | if ($httpMethod === 'HEAD') { |
||||
456 | 3 | $allowedMethods += ['GET' => 'GET']; |
|||
457 | } |
||||
458 | |||||
459 | foreach ($this->_routes as $scheme => $hostBasedRoutes) { |
||||
460 | if (!isset($allowedSchemes[$scheme])) { |
||||
461 | 1 | continue; |
|||
462 | } |
||||
463 | |||||
464 | foreach ($hostBasedRoutes as $routeHost => $methodBasedRoutes) { |
||||
465 | foreach ($methodBasedRoutes as $method => $routes) { |
||||
466 | if (!isset($allowedMethods[$method]) && $httpMethod !== '*') { |
||||
467 | 4 | continue; |
|||
468 | } |
||||
469 | foreach ($routes as $route) { |
||||
470 | /* @var $route \Lead\Router\RouteInterface */ |
||||
471 | if (!$route->match($request, $variables, $hostVariables)) { |
||||
472 | if ($hostVariables === null) { |
||||
473 | 3 | continue 3; |
|||
474 | } |
||||
475 | 10 | continue; |
|||
476 | } |
||||
477 | |||||
478 | 39 | return $route; |
|||
479 | } |
||||
480 | } |
||||
481 | } |
||||
482 | } |
||||
483 | |||||
484 | 13 | return null; |
|||
485 | } |
||||
486 | |||||
487 | /** |
||||
488 | * Middleware generator. |
||||
489 | * |
||||
490 | * @return callable |
||||
491 | */ |
||||
492 | public function middleware() |
||||
493 | { |
||||
494 | foreach ($this->_scopes[0]->middleware() as $middleware) { |
||||
495 | 1 | yield $middleware; |
|||
0 ignored issues
–
show
|
|||||
496 | } |
||||
497 | } |
||||
498 | |||||
499 | /** |
||||
500 | * Adds a middleware to the list of middleware. |
||||
501 | * |
||||
502 | * @param object|Closure A callable middleware. |
||||
0 ignored issues
–
show
|
|||||
503 | * @return $this |
||||
504 | */ |
||||
505 | public function apply($middleware) |
||||
506 | { |
||||
507 | foreach (func_get_args() as $mw) { |
||||
508 | 3 | $this->_scopes[0]->apply($mw); |
|||
509 | } |
||||
510 | |||||
511 | 3 | return $this; |
|||
512 | } |
||||
513 | |||||
514 | /** |
||||
515 | * Sets a dispatcher strategy |
||||
516 | * |
||||
517 | * @param string $name Name |
||||
518 | * @param callable $handler Handler |
||||
519 | * @return $this |
||||
520 | */ |
||||
521 | public function setStrategy(string $name, callable $handler) |
||||
522 | { |
||||
523 | 2 | $this->_strategies[$name] = $handler; |
|||
524 | |||||
525 | 2 | return $this; |
|||
526 | } |
||||
527 | |||||
528 | /** |
||||
529 | * Get a strategy |
||||
530 | * |
||||
531 | * @return callable |
||||
532 | */ |
||||
533 | public function getStrategy(string $name): callable |
||||
534 | { |
||||
535 | if (isset($this->_strategies[$name])) { |
||||
536 | 2 | return $this->_strategies[$name]; |
|||
537 | } |
||||
538 | |||||
539 | 16 | throw new RuntimeException(sprintf('Strategy `%s` not found.', $name)); |
|||
540 | } |
||||
541 | |||||
542 | /** |
||||
543 | * Unsets a strategy |
||||
544 | * |
||||
545 | * @param string $name |
||||
546 | * @return $this |
||||
547 | */ |
||||
548 | public function unsetStrategy(string $name) |
||||
549 | { |
||||
550 | if (isset($this->_strategies[$name])) { |
||||
551 | 1 | unset($this->_strategies[$name]); |
|||
552 | |||||
553 | 1 | return $this; |
|||
554 | } |
||||
555 | |||||
556 | throw new RuntimeException(sprintf('Strategy `%s` not found.', $name)); |
||||
557 | } |
||||
558 | |||||
559 | /** |
||||
560 | * Gets/sets router's strategies. |
||||
561 | * |
||||
562 | * @deprecated Use setStrategy(), unsetStrategy() and getStrategy() |
||||
563 | * @param string $name A routing strategy name. |
||||
564 | * @param mixed $handler The strategy handler or none to get the setted one. |
||||
565 | * @return mixed The strategy handler (or `null` if not found) on get or `$this` on set. |
||||
566 | */ |
||||
567 | public function strategy($name, $handler = null) |
||||
568 | { |
||||
569 | if (func_num_args() === 1) { |
||||
570 | try { |
||||
571 | 17 | return $this->getStrategy($name); |
|||
572 | } catch (RuntimeException $e) { |
||||
573 | 16 | return null; |
|||
574 | } |
||||
575 | } |
||||
576 | |||||
577 | if ($handler === false) { |
||||
578 | try { |
||||
579 | 1 | return $this->unsetStrategy($name); |
|||
580 | } catch (RuntimeException $e) { |
||||
581 | return null; |
||||
582 | } |
||||
583 | } |
||||
584 | |||||
585 | if (!$handler instanceof Closure && !method_exists($handler, '__invoke')) { |
||||
586 | 1 | throw new RouterException("The handler needs to be an instance of `Closure` or implements the `__invoke()` magic method."); |
|||
587 | } |
||||
588 | |||||
589 | 2 | return $this->setStrategy($name, $handler); |
|||
0 ignored issues
–
show
It seems like
$handler can also be of type object ; however, parameter $handler of Lead\Router\Router::setStrategy() 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
![]() |
|||||
590 | } |
||||
591 | |||||
592 | /** |
||||
593 | * Adds a route based on a custom HTTP verb. |
||||
594 | * |
||||
595 | * @param string $name The HTTP verb to define a route on. |
||||
596 | * @param array $params The route's parameters. |
||||
597 | * @return mixed |
||||
598 | */ |
||||
599 | public function __call($name, $params) |
||||
600 | { |
||||
601 | if ($strategy = $this->strategy($name)) { |
||||
0 ignored issues
–
show
The function
Lead\Router\Router::strategy() has been deprecated: Use setStrategy(), unsetStrategy() and getStrategy()
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
602 | 1 | array_unshift($params, $this); |
|||
603 | |||||
604 | 1 | return call_user_func_array($strategy, $params); |
|||
605 | } |
||||
606 | |||||
607 | if (is_callable($params[1])) { |
||||
608 | 11 | $params[2] = $params[1]; |
|||
609 | 11 | $params[1] = []; |
|||
610 | } |
||||
611 | |||||
612 | 15 | $params[1]['methods'] = [$name]; |
|||
613 | |||||
614 | 15 | return call_user_func_array([$this, 'bind'], $params); |
|||
615 | } |
||||
616 | |||||
617 | /** |
||||
618 | * Returns a route's link. |
||||
619 | * |
||||
620 | * @param string $name A route name. |
||||
621 | * @param array $params The route parameters. |
||||
622 | * @param array $options Options for generating the proper prefix. Accepted values are: |
||||
623 | * - `'absolute'` _boolean_: `true` or `false`. - `'scheme'` |
||||
624 | * _string_ : The scheme. - `'host'` _string_ : The host |
||||
625 | * name. - `'basePath'` _string_ : The base path. - `'query'` |
||||
626 | * _string_ : The query string. - `'fragment'` _string_ : The |
||||
627 | * fragment string. |
||||
628 | * |
||||
629 | * @return string The link. |
||||
630 | */ |
||||
631 | public function link(string $name, array $params = [], array $options = []): string |
||||
632 | { |
||||
633 | $defaults = [ |
||||
634 | 'basePath' => $this->getBasePath() |
||||
635 | 5 | ]; |
|||
636 | 5 | $options += $defaults; |
|||
637 | |||||
638 | 5 | $params += $this->_defaults; |
|||
639 | |||||
640 | if (!isset($this[$name])) { |
||||
641 | 1 | throw new RouterException("No binded route defined for `'{$name}'`, bind it first with `bind()`."); |
|||
642 | } |
||||
643 | 4 | $route = $this[$name]; |
|||
644 | |||||
645 | 4 | return $route->link($params, $options); |
|||
646 | } |
||||
647 | |||||
648 | /** |
||||
649 | * Clears the router. |
||||
650 | */ |
||||
651 | public function clear() |
||||
652 | { |
||||
653 | 1 | $this->_basePath = ''; |
|||
654 | 1 | $this->_strategies = []; |
|||
655 | 1 | $this->_defaults = []; |
|||
656 | 1 | $this->_routes = []; |
|||
657 | 1 | $scope = $this->_classes['scope']; |
|||
658 | 1 | $this->_scopes = [new $scope(['router' => $this])]; |
|||
659 | } |
||||
660 | |||||
661 | /** |
||||
662 | * Return the current element |
||||
663 | * |
||||
664 | * @link https://php.net/manual/en/iterator.current.php |
||||
665 | * @return mixed Can return any type. |
||||
666 | * @since 5.0.0 |
||||
667 | */ |
||||
668 | public function current() |
||||
669 | { |
||||
670 | return current($this->_data); |
||||
671 | } |
||||
672 | |||||
673 | /** |
||||
674 | * Move forward to next element |
||||
675 | * |
||||
676 | * @link https://php.net/manual/en/iterator.next.php |
||||
677 | * @return void Any returned value is ignored. |
||||
678 | * @since 5.0.0 |
||||
679 | */ |
||||
680 | public function next() |
||||
681 | { |
||||
682 | $value = $this->_skipNext ? current($this->_data) : next($this->_data); |
||||
683 | $this->_skipNext = false; |
||||
684 | |||||
685 | key($this->_data) !== null ? $value : null; |
||||
686 | } |
||||
687 | |||||
688 | /** |
||||
689 | * Return the key of the current element |
||||
690 | * |
||||
691 | * @link https://php.net/manual/en/iterator.key.php |
||||
692 | * @return mixed scalar on success, or null on failure. |
||||
693 | * @since 5.0.0 |
||||
694 | */ |
||||
695 | public function key() |
||||
696 | { |
||||
697 | return array_keys($this->_data); |
||||
698 | } |
||||
699 | |||||
700 | /** |
||||
701 | * Checks if current position is valid |
||||
702 | * |
||||
703 | * @link https://php.net/manual/en/iterator.valid.php |
||||
704 | * @return bool The return value will be casted to boolean and then evaluated. |
||||
705 | * Returns true on success or false on failure. |
||||
706 | * @since 5.0.0 |
||||
707 | */ |
||||
708 | public function valid() |
||||
709 | { |
||||
710 | return key($this->_data) !== null; |
||||
711 | } |
||||
712 | |||||
713 | /** |
||||
714 | * Rewind the Iterator to the first element |
||||
715 | * |
||||
716 | * @link https://php.net/manual/en/iterator.rewind.php |
||||
717 | * @return void Any returned value is ignored. |
||||
718 | * @since 5.0.0 |
||||
719 | */ |
||||
720 | public function rewind() |
||||
721 | { |
||||
722 | $this->_skipNext = false; |
||||
723 | |||||
724 | reset($this->_data); |
||||
725 | } |
||||
726 | |||||
727 | /** |
||||
728 | * Whether a offset exists |
||||
729 | * |
||||
730 | * @link https://php.net/manual/en/arrayaccess.offsetexists.php |
||||
731 | * @param mixed $offset <p> |
||||
732 | * An offset to check for. |
||||
733 | * </p> |
||||
734 | * @return bool true on success or false on failure. |
||||
735 | * </p> |
||||
736 | * <p> |
||||
737 | * The return value will be casted to boolean if non-boolean was returned. |
||||
738 | * @since 5.0.0 |
||||
739 | */ |
||||
740 | public function offsetExists($offset) |
||||
741 | { |
||||
742 | 8 | return array_key_exists($offset, $this->_data); |
|||
743 | } |
||||
744 | |||||
745 | /** |
||||
746 | * Offset to retrieve |
||||
747 | * |
||||
748 | * @link https://php.net/manual/en/arrayaccess.offsetget.php |
||||
749 | * @param mixed $offset <p> |
||||
750 | * The offset to retrieve. |
||||
751 | * </p> |
||||
752 | * @return mixed Can return all value types. |
||||
753 | * @since 5.0.0 |
||||
754 | */ |
||||
755 | public function offsetGet($offset) |
||||
756 | { |
||||
757 | 7 | return $this->_data[$offset]; |
|||
758 | } |
||||
759 | |||||
760 | /** |
||||
761 | * Offset to set |
||||
762 | * |
||||
763 | * @link https://php.net/manual/en/arrayaccess.offsetset.php |
||||
764 | * @param mixed $offset <p> |
||||
765 | * The offset to assign the value to. |
||||
766 | * </p> |
||||
767 | * @param mixed $value <p> |
||||
768 | * The |
||||
769 | * value |
||||
770 | * to |
||||
771 | * set. |
||||
772 | * </p> |
||||
773 | * |
||||
774 | * @return void |
||||
775 | * @since 5.0.0 |
||||
776 | */ |
||||
777 | public function offsetSet($offset, $value) |
||||
778 | { |
||||
779 | if (is_null($offset)) { |
||||
780 | $this->_data[] = $value; |
||||
781 | return; |
||||
782 | } |
||||
783 | |||||
784 | $this->_data[$offset] = $value; |
||||
785 | } |
||||
786 | |||||
787 | /** |
||||
788 | * Offset to unset |
||||
789 | * |
||||
790 | * @link https://php.net/manual/en/arrayaccess.offsetunset.php |
||||
791 | * @param mixed $offset <p> |
||||
792 | * The offset to unset. |
||||
793 | * </p> |
||||
794 | * @return void |
||||
795 | * @since 5.0.0 |
||||
796 | */ |
||||
797 | public function offsetUnset($offset) |
||||
798 | { |
||||
799 | $this->_skipNext = $offset === key($this->_data); |
||||
800 | unset($this->_data[$offset]); |
||||
801 | } |
||||
802 | |||||
803 | /** |
||||
804 | * Count elements of an object |
||||
805 | * |
||||
806 | * @link https://php.net/manual/en/countable.count.php |
||||
807 | * @return int The custom count as an integer. |
||||
808 | * </p> |
||||
809 | * <p> |
||||
810 | * The return value is cast to an integer. |
||||
811 | * @since 5.1.0 |
||||
812 | */ |
||||
813 | /** |
||||
814 | * Counts the items of the object. |
||||
815 | * |
||||
816 | * @return integer Returns the number of items in the collection. |
||||
817 | */ |
||||
818 | public function count() |
||||
819 | { |
||||
820 | return count($this->_data); |
||||
821 | } |
||||
822 | } |
||||
823 |