These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Anax\Route; |
||
4 | |||
5 | use Anax\DI\InjectionAwareInterface; |
||
6 | use Anax\DI\InjectionAwareTrait; |
||
7 | use Anax\Route\Exception\ForbiddenException; |
||
8 | use Anax\Route\Exception\NotFoundException; |
||
9 | use Anax\Route\Exception\InternalErrorException; |
||
10 | use Anax\Route\Exception\ConfigurationException; |
||
11 | |||
12 | /** |
||
13 | * A container for routes. |
||
14 | */ |
||
15 | class Router implements |
||
16 | InjectionAwareInterface |
||
17 | { |
||
18 | use InjectionAwareTrait; |
||
19 | |||
20 | |||
21 | |||
22 | /** |
||
23 | * @var array $routes all the routes. |
||
24 | * @var array $internalRoutes all internal routes. |
||
25 | * @var null|string $lastRoute last route that was matched and called. |
||
26 | */ |
||
27 | private $routes = []; |
||
28 | private $internalRoutes = []; |
||
29 | private $lastRoute = null; |
||
30 | |||
31 | |||
32 | |||
33 | /** |
||
34 | * @const DEVELOPMENT Verbose with exceptions. |
||
35 | * @const PRODUCTION Exceptions turns into 500. |
||
36 | */ |
||
37 | const DEVELOPMENT = 0; |
||
38 | const PRODUCTION = 1; |
||
39 | |||
40 | |||
41 | |||
42 | /** |
||
43 | * @var integer $mode current mode. |
||
44 | */ |
||
45 | private $mode = self::DEVELOPMENT; |
||
46 | |||
47 | |||
48 | |||
49 | /** |
||
50 | * Set Router::DEVELOPMENT or Router::PRODUCTION mode. |
||
51 | * |
||
52 | * @param integer $mode which mode to set. |
||
53 | * |
||
54 | * @return self to enable chaining. |
||
55 | */ |
||
56 | public function setMode(integer $mode) : object |
||
57 | { |
||
58 | $this->mode = $mode; |
||
0 ignored issues
–
show
|
|||
59 | return $this; |
||
60 | } |
||
61 | |||
62 | |||
63 | |||
64 | /** |
||
65 | * Add routes from an array where the array looks like this: |
||
66 | * [ |
||
67 | * "mount" => null|string, // Where to mount the routes |
||
68 | * "routes" => [ // All routes in this array |
||
69 | * [ |
||
70 | * "info" => "Just say hi.", |
||
71 | * "method" => null, |
||
72 | * "path" => "hi", |
||
73 | * "handler" => function () { |
||
74 | * return "Hi."; |
||
75 | * }, |
||
76 | * ] |
||
77 | * ] |
||
78 | * ] |
||
79 | * |
||
80 | * @throws ConfigurationException |
||
81 | * |
||
82 | * @param array $routes containing the routes to add. |
||
83 | * |
||
84 | * @return self to enable chaining. |
||
85 | */ |
||
86 | public function addRoutes(array $routes) : object |
||
87 | { |
||
88 | $mount = null; |
||
89 | if (isset($routes["mount"])) { |
||
90 | $mount = rtrim($routes["mount"], "/"); |
||
91 | if (!empty($mount)) { |
||
92 | $mount .= "/"; |
||
93 | } |
||
94 | } |
||
95 | |||
96 | if (!(isset($routes["routes"]) && is_array($routes["routes"]))) { |
||
97 | throw new ConfigurationException(t("No routes found, missing key 'routes' in configuration array.")); |
||
98 | } |
||
99 | |||
100 | foreach ($routes["routes"] as $route) { |
||
101 | if ($route["internal"] ?? false) { |
||
102 | $this->addInternalRoute( |
||
103 | $route["path"], |
||
104 | $route["handler"] ?? null |
||
105 | ); |
||
106 | continue; |
||
107 | } |
||
108 | |||
109 | if (!array_key_exists("path", $route)) { |
||
110 | throw new ConfigurationException(t("Creating route but path is not defined for route.")); |
||
111 | } |
||
112 | |||
113 | $this->addRoute( |
||
114 | $route["method"] ?? null, |
||
115 | $mount . $route["path"], |
||
116 | $route["handler"] ?? null, |
||
117 | $route["info"] ?? null |
||
118 | ); |
||
119 | } |
||
120 | |||
121 | return $this; |
||
122 | } |
||
123 | |||
124 | |||
125 | |||
126 | /** |
||
127 | * Add a route with a request method, a path rule to match and an action |
||
128 | * as the callback. Adding several path rules (array) results in several |
||
129 | * routes being created. |
||
130 | * |
||
131 | * @param string|array $method as request method to support |
||
132 | * @param string $path for this route |
||
133 | * @param string|array|callable $handler for this path, callable or equal |
||
134 | * @param string $info description of the route |
||
135 | * |
||
136 | * @return void. |
||
137 | */ |
||
138 | protected function addRoute( |
||
139 | $method, |
||
140 | string $path = null, |
||
141 | $handler = null, |
||
142 | string $info = null |
||
143 | ) : void |
||
144 | { |
||
145 | $route = new Route(); |
||
146 | $route->set($method, $path, $handler, $info); |
||
147 | $this->routes[] = $route; |
||
148 | } |
||
149 | |||
150 | |||
151 | |||
152 | /** |
||
153 | * Add an internal route to the router, this route is not exposed to the |
||
154 | * browser and the end user. |
||
155 | * |
||
156 | * @param string $path for this route |
||
157 | * @param string|array|callable $handler for this path, callable or equal |
||
158 | * |
||
159 | * @return void. |
||
0 ignored issues
–
show
The doc-type
void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.
Loading history...
|
|||
160 | */ |
||
161 | public function addInternalRoute(string $path, $handler) : void |
||
162 | { |
||
163 | $route = new Route(); |
||
164 | $route->set($path, $handler); |
||
0 ignored issues
–
show
$handler is of type callable , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
Loading history...
|
|||
165 | $this->internalRoutes[$path] = $route; |
||
166 | } |
||
167 | |||
168 | |||
169 | |||
170 | /** |
||
171 | * Load route from an array contining route details. |
||
172 | * |
||
173 | * @throws ConfigurationException |
||
174 | * |
||
175 | * @param array $route details on the route. |
||
176 | * |
||
177 | * @return self |
||
178 | * |
||
179 | * @SuppressWarnings(PHPMD.UnusedLocalVariable) |
||
180 | */ |
||
181 | public function load(array $route) : object |
||
182 | { |
||
183 | var_dump($route); |
||
0 ignored issues
–
show
|
|||
184 | |||
185 | $mount = isset($route["mount"]) ? rtrim($route["mount"], "/") : null; |
||
186 | |||
187 | $config = $route; |
||
188 | |||
189 | // Include the config file and load its routes |
||
190 | $config = require($file); |
||
0 ignored issues
–
show
|
|||
191 | $routes = isset($config["routes"]) ? $config["routes"] : []; |
||
192 | foreach ($routes as $route) { |
||
193 | $path = isset($mount) |
||
194 | ? $mount . "/" . $route["path"] |
||
195 | : $route["path"]; |
||
196 | |||
197 | if (isset($route["internal"]) && $route["internal"]) { |
||
198 | $this->addInternal($path, $route["callable"]); |
||
0 ignored issues
–
show
The method
addInternal() does not exist on Anax\Route\Router . Did you maybe mean addInternalRoute() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise.
Loading history...
|
|||
199 | continue; |
||
200 | } |
||
201 | |||
202 | $this->any( |
||
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
203 | $route["requestMethod"], |
||
204 | $path, |
||
205 | $route["callable"], |
||
206 | $route["info"] |
||
207 | ); |
||
208 | } |
||
209 | |||
210 | return $this; |
||
211 | } |
||
212 | |||
213 | |||
214 | |||
215 | /** |
||
216 | * Load and apply configurations. |
||
217 | * |
||
218 | * @param array|string $what is an array with key/value config options |
||
219 | * or a file to be included which returns such |
||
220 | * an array. |
||
221 | * |
||
222 | * @return self |
||
223 | */ |
||
224 | public function configure($what) |
||
225 | { |
||
226 | $this->configure2($what); |
||
0 ignored issues
–
show
|
|||
227 | $includes = $this->getConfig("routeFiles", []); |
||
0 ignored issues
–
show
The method
getConfig() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
228 | $items = $this->getConfig("items", []); |
||
0 ignored issues
–
show
The method
getConfig() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
229 | $config = array_merge($includes, $items); |
||
230 | |||
231 | // Add a sort field if missing, to maintain order |
||
232 | // when sorting |
||
233 | $sort = 1; |
||
234 | array_walk($config, function (&$item) use (&$sort) { |
||
235 | $item["sort"] = (isset($item["sort"])) |
||
236 | ? $item["sort"] |
||
237 | : $sort++; |
||
238 | }); |
||
239 | uasort($config, function ($item1, $item2) { |
||
240 | if ($item1["sort"] === $item2["sort"]) { |
||
241 | return 0; |
||
242 | } |
||
243 | return ($item1["sort"] < $item2["sort"]) ? -1 : 1; |
||
244 | }); |
||
245 | |||
246 | foreach ($config as $route) { |
||
247 | $this->load($route); |
||
248 | } |
||
249 | |||
250 | return $this; |
||
251 | } |
||
252 | |||
253 | |||
254 | |||
255 | /** |
||
256 | * Handle the routes and match them towards the request, dispatch them |
||
257 | * when a match is made. Each route handler may throw exceptions that |
||
258 | * may redirect to an internal route for error handling. |
||
259 | * Several routes can match and if the routehandler does not break |
||
260 | * execution flow, the route matching will carry on. |
||
261 | * Only the last routehandler will get its return value returned further. |
||
262 | * |
||
263 | * @param string $path the path to find a matching handler for. |
||
264 | * @param string $method the request method to match. |
||
265 | * |
||
266 | * @return mixed content returned from route. |
||
267 | */ |
||
268 | public function handle($path, $method = null) |
||
269 | { |
||
270 | try { |
||
271 | $match = false; |
||
272 | foreach ($this->routes as $route) { |
||
273 | if ($route->match($path, $method)) { |
||
274 | $this->lastRoute = $route->getRule(); |
||
275 | $match = true; |
||
276 | $results = $route->handle($this->di); |
||
277 | if ($results) { |
||
278 | return $results; |
||
279 | } |
||
280 | } |
||
281 | } |
||
282 | |||
283 | $this->handleInternal("404"); |
||
284 | } catch (ForbiddenException $e) { |
||
285 | $this->handleInternal("403"); |
||
286 | } catch (NotFoundException $e) { |
||
287 | $this->handleInternal("404"); |
||
288 | } catch (InternalErrorException $e) { |
||
289 | $this->handleInternal("500"); |
||
290 | } catch (ConfigurationException $e) { |
||
291 | if ($this->mode === Router::PRODUCTION) { |
||
292 | $this->handleInternal("500"); |
||
293 | } |
||
294 | throw $e; |
||
295 | } |
||
296 | } |
||
297 | |||
298 | |||
299 | |||
300 | /** |
||
301 | * Handle an internal route, the internal routes are not exposed to the |
||
302 | * end user. |
||
303 | * |
||
304 | * @param string $rule for this route. |
||
305 | * |
||
306 | * @throws \Anax\Route\Exception\NotFoundException |
||
307 | * |
||
308 | * @return void |
||
309 | */ |
||
310 | public function handleInternal($rule) |
||
311 | { |
||
312 | if (!isset($this->internalRoutes[$rule])) { |
||
313 | throw new NotFoundException("No internal route to handle: " . $rule); |
||
314 | } |
||
315 | $route = $this->internalRoutes[$rule]; |
||
316 | $this->lastRoute = $rule; |
||
317 | $route->handle($this->di); |
||
318 | } |
||
319 | |||
320 | |||
321 | |||
322 | /** |
||
323 | * Add a route to the router by rule(s) and a callback. |
||
324 | * |
||
325 | * @param null|string|array $rule for this route. |
||
326 | * @param null|string|callable $action a callback handler for the route. |
||
327 | * |
||
328 | * @return class|array as new route(s), class if one added, else array. |
||
329 | */ |
||
330 | 6 | public function add($rule, $action = null) |
|
331 | { |
||
332 | 6 | return $this->any(null, $rule, $action); |
|
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
333 | } |
||
334 | |||
335 | |||
336 | |||
337 | /** |
||
338 | * Add a default route which will be applied for any path. |
||
339 | * |
||
340 | * @param string|callable $action a callback handler for the route. |
||
341 | * |
||
342 | * @return class as new route. |
||
343 | */ |
||
344 | 1 | public function always($action) |
|
345 | { |
||
346 | 1 | return $this->any(null, null, $action); |
|
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
347 | } |
||
348 | |||
349 | |||
350 | |||
351 | /** |
||
352 | * Add a default route which will be applied for any path, if the choosen |
||
353 | * request method is matching. |
||
354 | * |
||
355 | * @param null|string|array $method as request methods |
||
356 | * @param null|string|callable $action a callback handler for the route. |
||
357 | * |
||
358 | * @return class|array as new route(s), class if one added, else array. |
||
359 | */ |
||
360 | 1 | public function all($method, $action) |
|
361 | { |
||
362 | 1 | return $this->any($method, null, $action); |
|
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
363 | } |
||
364 | |||
365 | |||
366 | |||
367 | /** |
||
368 | * Shortcut to add a GET route. |
||
369 | * |
||
370 | * @param null|string|array $method as request methods |
||
0 ignored issues
–
show
There is no parameter named
$method . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
371 | * @param null|string|callable $action a callback handler for the route. |
||
372 | * |
||
373 | * @return class|array as new route(s), class if one added, else array. |
||
374 | */ |
||
375 | public function get($rule, $action) |
||
376 | { |
||
377 | return $this->any(["GET"], $rule, $action); |
||
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
378 | } |
||
379 | |||
380 | |||
381 | |||
382 | /** |
||
383 | * Shortcut to add a POST route. |
||
384 | * |
||
385 | * @param null|string|array $method as request methods |
||
0 ignored issues
–
show
There is no parameter named
$method . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
386 | * @param null|string|callable $action a callback handler for the route. |
||
387 | * |
||
388 | * @return class|array as new route(s), class if one added, else array. |
||
389 | */ |
||
390 | public function post($rule, $action) |
||
391 | { |
||
392 | return $this->any(["POST"], $rule, $action); |
||
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
393 | } |
||
394 | |||
395 | |||
396 | |||
397 | /** |
||
398 | * Shortcut to add a PUT route. |
||
399 | * |
||
400 | * @param null|string|array $method as request methods |
||
0 ignored issues
–
show
There is no parameter named
$method . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
401 | * @param null|string|callable $action a callback handler for the route. |
||
402 | * |
||
403 | * @return class|array as new route(s), class if one added, else array. |
||
404 | */ |
||
405 | public function put($rule, $action) |
||
406 | { |
||
407 | return $this->any(["PUT"], $rule, $action); |
||
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
408 | } |
||
409 | |||
410 | |||
411 | |||
412 | /** |
||
413 | * Shortcut to add a DELETE route. |
||
414 | * |
||
415 | * @param null|string|array $method as request methods |
||
0 ignored issues
–
show
There is no parameter named
$method . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
416 | * @param null|string|callable $action a callback handler for the route. |
||
417 | * |
||
418 | * @return class|array as new route(s), class if one added, else array. |
||
419 | */ |
||
420 | public function delete($rule, $action) |
||
421 | { |
||
422 | return $this->any(["DELETE"], $rule, $action); |
||
0 ignored issues
–
show
The method
any() does not seem to exist on object<Anax\Route\Router> .
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...
|
|||
423 | } |
||
424 | |||
425 | |||
426 | |||
427 | /** |
||
428 | * Get the route for the last route that was handled. |
||
429 | * |
||
430 | * @return mixed |
||
431 | */ |
||
432 | public function getLastRoute() |
||
433 | { |
||
434 | return $this->lastRoute; |
||
435 | } |
||
436 | |||
437 | |||
438 | |||
439 | /** |
||
440 | * Get all routes. |
||
441 | * |
||
442 | * @return array with all routes. |
||
443 | */ |
||
444 | public function getAll() |
||
445 | { |
||
446 | return $this->routes; |
||
447 | } |
||
448 | |||
449 | |||
450 | |||
451 | /** |
||
452 | * Get all internal routes. |
||
453 | * |
||
454 | * @return array with internal routes. |
||
455 | */ |
||
456 | public function getInternal() |
||
457 | { |
||
458 | return $this->internalRoutes; |
||
459 | } |
||
460 | } |
||
461 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..