This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
Check that an empty catch block is always commented
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\Commons\ContainerInjectableInterface; |
||
6 | use Anax\Route\Exception\ConfigurationException; |
||
7 | use Anax\Route\Exception\NotFoundException; |
||
8 | use Psr\Container\ContainerInterface; |
||
9 | |||
10 | /** |
||
11 | * Call a routes handler and return the results. |
||
12 | */ |
||
13 | class RouteHandler |
||
14 | { |
||
15 | /** |
||
16 | * @var ContainerInterface $di the dependency/service container. |
||
17 | */ |
||
18 | protected $di; |
||
19 | |||
20 | |||
21 | |||
22 | /** |
||
23 | * Handle the action for a route and return the results. |
||
24 | * |
||
25 | * @param string $method the request method. |
||
26 | * @param string $path that was matched. |
||
27 | * @param string|array $action base for the callable. |
||
28 | * @param array $arguments optional arguments. |
||
29 | * @param ContainerInjectableInterface $di container with services. |
||
30 | * |
||
31 | * @return mixed as the result from the route handler. |
||
32 | */ |
||
33 | 149 | public function handle( |
|
34 | ?string $method, |
||
35 | ?string $path, |
||
36 | $action, |
||
37 | array $arguments = [], |
||
38 | ContainerInterface $di = null |
||
39 | ) { |
||
40 | 149 | $this->di = $di; |
|
41 | |||
42 | 149 | if (is_null($action)) { |
|
43 | 1 | return; |
|
44 | } |
||
45 | |||
46 | 148 | if (is_callable($action)) { |
|
47 | 118 | if (is_array($action) |
|
48 | 118 | && is_string($action[0]) |
|
49 | 118 | && class_exists($action[0]) |
|
50 | ) { |
||
51 | 2 | $action[] = $arguments; |
|
52 | 2 | return $this->handleAsControllerAction($action); |
|
53 | } |
||
54 | 116 | return $this->handleAsCallable($action, $arguments); |
|
55 | } |
||
56 | |||
57 | 30 | if (is_string($action) && class_exists($action)) { |
|
58 | 26 | $callable = $this->isControllerAction($method, $path, $action); |
|
59 | 25 | if ($callable) { |
|
60 | 21 | return $this->handleAsControllerAction($callable); |
|
61 | } |
||
62 | |||
63 | 4 | $isinvocable = $this->isInvocableClass($action); |
|
64 | 4 | if ($isinvocable) { |
|
65 | 3 | return $this->handleAsInvocableClass($action); |
|
66 | } |
||
67 | } |
||
68 | |||
69 | 5 | if ($di |
|
70 | 5 | && is_array($action) |
|
71 | 5 | && isset($action[0]) |
|
72 | 5 | && isset($action[1]) |
|
73 | 5 | && is_string($action[0]) |
|
74 | ) { |
||
75 | // Try to load service from app/di injected container |
||
76 | 3 | return $this->handleUsingDi($action, $arguments, $di); |
|
77 | } |
||
78 | |||
79 | 2 | throw new ConfigurationException("Handler for route does not seem to be a callable action."); |
|
80 | } |
||
81 | |||
82 | |||
83 | |||
84 | /** |
||
85 | * Get an informative string representing the handler type. |
||
86 | * |
||
87 | * @param string|array $action base for the callable. |
||
88 | * @param ContainerInjectableInterface $di container with services. |
||
89 | * |
||
90 | * @return string as the type of handler. |
||
91 | */ |
||
92 | 2 | public function getHandlerType( |
|
93 | $action, |
||
94 | ContainerInterface $di = null |
||
95 | ) { |
||
96 | 2 | if (is_null($action)) { |
|
97 | 1 | return "null"; |
|
98 | } |
||
99 | |||
100 | 2 | if (is_callable($action)) { |
|
101 | 1 | return "callable"; |
|
102 | } |
||
103 | |||
104 | 2 | if (is_string($action) && class_exists($action)) { |
|
105 | 1 | $callable = $this->isControllerAction(null, null, $action); |
|
106 | 1 | if ($callable) { |
|
107 | 1 | return "controller"; |
|
108 | } |
||
109 | } |
||
110 | |||
111 | 2 | if ($di |
|
112 | 2 | && is_array($action) |
|
113 | 2 | && isset($action[0]) |
|
114 | 2 | && isset($action[1]) |
|
115 | 2 | && is_string($action[0]) |
|
116 | 2 | && $di->has($action[0]) |
|
117 | 2 | && is_callable([$di->get($action[0]), $action[1]]) |
|
118 | ) { |
||
119 | 1 | return "di"; |
|
120 | } |
||
121 | |||
122 | 1 | return "not found"; |
|
123 | } |
||
124 | |||
125 | |||
126 | |||
127 | /** |
||
128 | * Check if action is a class with the magic method __invoke. |
||
129 | * |
||
130 | * @param string $action the proposed handler. |
||
131 | * |
||
132 | * @return boolean true if class has implemented __invoke, else false. |
||
133 | */ |
||
134 | 4 | protected function isInvocableClass(string $action) : bool |
|
135 | { |
||
136 | 4 | $rc = new \ReflectionClass($action); |
|
137 | 4 | return $rc->hasMethod("__invoke"); |
|
138 | } |
||
139 | |||
140 | |||
141 | |||
142 | /** |
||
143 | * Call the __invoke action with optional arguments and call |
||
144 | * initialisation methods if available. |
||
145 | * |
||
146 | * @param string $class as class that implements __invokable. |
||
147 | * |
||
148 | * @return mixed result from the handler. |
||
149 | */ |
||
150 | 3 | protected function handleAsInvocableClass(string $class) |
|
151 | { |
||
152 | 3 | $obj = new $class(); |
|
153 | // $class = $callable[0]; |
||
154 | 3 | $action = "__invoke"; |
|
155 | // $args = $callable[2]; |
||
156 | |||
157 | 3 | $refl = new \ReflectionClass($class); |
|
158 | 3 | $diInterface = "Anax\Commons\ContainerInjectableInterface"; |
|
159 | 3 | $appInterface = "Anax\Commons\AppInjectableInterface"; |
|
160 | |||
161 | 3 | if ($this->di && $refl->implementsInterface($diInterface)) { |
|
162 | 1 | $obj->setDI($this->di); |
|
163 | 2 | } elseif ($this->di && $refl->implementsInterface($appInterface)) { |
|
164 | if (!$this->di->has("app")) { |
||
165 | throw new ConfigurationException( |
||
166 | "Controller '$class' implements AppInjectableInterface but \$app is not available in \$di." |
||
167 | ); |
||
168 | } |
||
169 | $obj->setApp($this->di->get("app")); |
||
170 | } |
||
171 | |||
172 | try { |
||
173 | 3 | $refl = new \ReflectionMethod($class, "initialize"); |
|
174 | 2 | if ($refl->isPublic()) { |
|
175 | 2 | $res = $obj->initialize(); |
|
176 | 2 | if (!is_null($res)) { |
|
177 | 2 | return $res; |
|
178 | } |
||
179 | } |
||
180 | 1 | } catch (\ReflectionException $e) { |
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
Loading history...
|
|||
181 | ; |
||
182 | } |
||
183 | |||
184 | 2 | $refl = new \ReflectionMethod($obj, $action); |
|
185 | 2 | $paramIsVariadic = false; |
|
186 | 2 | foreach ($refl->getParameters() as $param) { |
|
187 | if ($param->isVariadic()) { |
||
188 | $paramIsVariadic = true; |
||
189 | break; |
||
190 | } |
||
191 | } |
||
192 | |||
193 | // if (!$paramIsVariadic |
||
194 | // && $refl->getNumberOfParameters() < count($args) |
||
195 | // ) { |
||
196 | // throw new NotFoundException( |
||
197 | // "Controller '$class' with action method '$action' valid but to many parameters. Got " |
||
198 | // . count($args) |
||
199 | // . ", expected " |
||
200 | // . $refl->getNumberOfParameters() . "." |
||
201 | // ); |
||
202 | // } |
||
203 | |||
204 | try { |
||
205 | //$res = $obj(...$args); |
||
206 | 2 | $res = $obj(); |
|
207 | } catch (\ArgumentCountError $e) { |
||
208 | throw new NotFoundException($e->getMessage()); |
||
209 | } catch (\TypeError $e) { |
||
210 | throw new NotFoundException($e->getMessage()); |
||
211 | } |
||
212 | |||
213 | 2 | return $res; |
|
214 | } |
||
215 | |||
216 | |||
217 | |||
218 | /** |
||
219 | * Check if items can be used to call a controller action, verify |
||
220 | * that the controller exists, the action has a class-method to call. |
||
221 | * |
||
222 | * @param string $method the request method. |
||
223 | * @param string $path the matched path, base for the controller action |
||
224 | * and the arguments. |
||
225 | * @param string $class the controller class |
||
226 | * |
||
227 | * @return array with callable details. |
||
228 | */ |
||
229 | 27 | protected function isControllerAction( |
|
230 | ?string $method, |
||
231 | ?string $path, |
||
232 | string $class |
||
233 | ) { |
||
234 | 27 | $method = ucfirst(strtolower($method)); |
|
235 | 27 | $args = explode("/", $path); |
|
236 | 27 | $action = array_shift($args); |
|
237 | 27 | $action = empty($action) ? "index" : $action; |
|
238 | 27 | $action = str_replace("-", "", $action); |
|
239 | 27 | $action1 = "{$action}Action{$method}"; |
|
240 | 27 | $action2 = "{$action}Action"; |
|
241 | 27 | $action3 = "catchAll{$method}"; |
|
242 | 27 | $action4 = "catchAll"; |
|
243 | |||
244 | 27 | foreach ([$action1, $action2] as $target) { |
|
245 | try { |
||
246 | 27 | $refl = new \ReflectionMethod($class, $target); |
|
247 | 19 | if (!$refl->isPublic()) { |
|
248 | 1 | throw new NotFoundException("Controller method '$class::$target' is not a public method."); |
|
249 | } |
||
250 | |||
251 | 18 | return [$class, $target, $args]; |
|
252 | 17 | } catch (\ReflectionException $e) { |
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
253 | ; |
||
254 | } |
||
255 | } |
||
256 | |||
257 | 8 | foreach ([$action3, $action4] as $target) { |
|
258 | try { |
||
259 | 8 | $refl = new \ReflectionMethod($class, $target); |
|
260 | 4 | if (!$refl->isPublic()) { |
|
261 | throw new NotFoundException("Controller method '$class::$target' is not a public method."); |
||
262 | } |
||
263 | |||
264 | 4 | array_unshift($args, $action); |
|
265 | 4 | return [$class, $target, $args]; |
|
266 | 5 | } catch (\ReflectionException $e) { |
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
267 | ; |
||
268 | } |
||
269 | } |
||
270 | |||
271 | 4 | return false; |
|
272 | } |
||
273 | |||
274 | |||
275 | |||
276 | /** |
||
277 | * Call the controller action with optional arguments and call |
||
278 | * initialisation methods if available. |
||
279 | * |
||
280 | * @param string $callable with details on what controller action to call. |
||
281 | * |
||
282 | * @return mixed result from the handler. |
||
283 | */ |
||
284 | 23 | protected function handleAsControllerAction(array $callable) |
|
285 | { |
||
286 | 23 | $class = $callable[0]; |
|
287 | 23 | $action = $callable[1]; |
|
288 | 23 | $args = $callable[2]; |
|
289 | 23 | $obj = new $class(); |
|
290 | |||
291 | 23 | $refl = new \ReflectionClass($class); |
|
292 | 23 | $diInterface = "Anax\Commons\ContainerInjectableInterface"; |
|
293 | 23 | $appInterface = "Anax\Commons\AppInjectableInterface"; |
|
294 | |||
295 | 23 | if ($this->di && $refl->implementsInterface($diInterface)) { |
|
296 | 1 | $obj->setDI($this->di); |
|
297 | 22 | } elseif ($this->di && $refl->implementsInterface($appInterface)) { |
|
298 | 2 | if (!$this->di->has("app")) { |
|
299 | 1 | throw new ConfigurationException( |
|
300 | 1 | "Controller '$class' implements AppInjectableInterface but \$app is not available in \$di." |
|
301 | ); |
||
302 | } |
||
303 | 1 | $obj->setApp($this->di->get("app")); |
|
304 | } |
||
305 | |||
306 | try { |
||
307 | 22 | $refl = new \ReflectionMethod($class, "initialize"); |
|
308 | 15 | if ($refl->isPublic()) { |
|
309 | 15 | $res = $obj->initialize(); |
|
310 | 15 | if (!is_null($res)) { |
|
311 | 15 | return $res; |
|
312 | } |
||
313 | } |
||
314 | 7 | } catch (\ReflectionException $e) { |
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
315 | ; |
||
316 | } |
||
317 | |||
318 | 21 | $refl = new \ReflectionMethod($obj, $action); |
|
319 | 21 | $paramIsVariadic = false; |
|
320 | 21 | foreach ($refl->getParameters() as $param) { |
|
321 | 9 | if ($param->isVariadic()) { |
|
322 | 4 | $paramIsVariadic = true; |
|
323 | 9 | break; |
|
324 | } |
||
325 | } |
||
326 | |||
327 | 21 | if (!$paramIsVariadic |
|
328 | 21 | && $refl->getNumberOfParameters() < count($args) |
|
329 | ) { |
||
330 | 1 | throw new NotFoundException( |
|
331 | 1 | "Controller '$class' with action method '$action' valid but to many parameters. Got " |
|
332 | 1 | . count($args) |
|
333 | 1 | . ", expected " |
|
334 | 1 | . $refl->getNumberOfParameters() . "." |
|
335 | ); |
||
336 | } |
||
337 | |||
338 | try { |
||
339 | 20 | $res = $obj->$action(...$args); |
|
340 | 2 | } catch (\ArgumentCountError $e) { |
|
341 | 1 | throw new NotFoundException($e->getMessage()); |
|
342 | 1 | } catch (\TypeError $e) { |
|
343 | 1 | throw new NotFoundException($e->getMessage()); |
|
344 | } |
||
345 | |||
346 | 18 | return $res; |
|
347 | } |
||
348 | |||
349 | |||
350 | |||
351 | /** |
||
352 | * Handle as callable support callables where the method is not static. |
||
353 | * |
||
354 | * @param string|array $action base for the callable |
||
355 | * @param array $arguments optional arguments |
||
356 | * @param ContainerInjectableInterface $di container with services |
||
357 | * |
||
358 | * @return mixed as the result from the route handler. |
||
359 | */ |
||
360 | 116 | protected function handleAsCallable( |
|
361 | $action, |
||
362 | array $arguments |
||
363 | ) { |
||
364 | 116 | if (is_array($action) |
|
365 | 116 | && isset($action[0]) |
|
366 | 116 | && isset($action[1]) |
|
367 | 116 | && is_string($action[0]) |
|
368 | 116 | && is_string($action[1]) |
|
369 | 116 | && class_exists($action[0]) |
|
370 | ) { |
||
371 | // ["SomeClass", "someMethod"] but not static |
||
372 | $refl = new \ReflectionMethod($action[0], $action[1]); |
||
373 | if ($refl->isPublic() && !$refl->isStatic()) { |
||
374 | $obj = new $action[0](); |
||
375 | return $obj->{$action[1]}(...$arguments); |
||
376 | } |
||
377 | } |
||
378 | |||
379 | // Add $di to param list, if defined by the callback |
||
380 | 116 | $refl = is_array($action) |
|
381 | 1 | ? new \ReflectionMethod($action[0], $action[1]) |
|
382 | 116 | : new \ReflectionFunction($action); |
|
383 | 116 | $params = $refl->getParameters(); |
|
384 | 116 | if (isset($params[0]) && $params[0]->getName() === "di") { |
|
385 | 1 | array_unshift($arguments, $this->di); |
|
386 | } |
||
387 | |||
388 | 116 | return call_user_func($action, ...$arguments); |
|
389 | } |
||
390 | |||
391 | |||
392 | |||
393 | /** |
||
394 | * Load callable as a service from the $di container. |
||
395 | * |
||
396 | * @param string|array $action base for the callable |
||
397 | * @param array $arguments optional arguments |
||
398 | * @param ContainerInjectableInterface $di container with services |
||
399 | * |
||
400 | * @return mixed as the result from the route handler. |
||
401 | */ |
||
402 | 3 | protected function handleUsingDi( |
|
403 | $action, |
||
404 | array $arguments, |
||
405 | ContainerInterface $di |
||
406 | ) { |
||
407 | 3 | if (!$di->has($action[0])) { |
|
408 | 1 | throw new ConfigurationException("Routehandler '{$action[0]}' not loaded in di."); |
|
409 | } |
||
410 | |||
411 | 2 | $service = $di->get($action[0]); |
|
412 | 2 | if (!is_callable([$service, $action[1]])) { |
|
413 | 1 | throw new ConfigurationException( |
|
414 | 1 | "Routehandler '{$action[0]}' does not have a callable method '{$action[1]}'." |
|
415 | ); |
||
416 | } |
||
417 | |||
418 | 1 | return call_user_func( |
|
419 | 1 | [$service, $action[1]], |
|
420 | 1 | ...$arguments |
|
421 | ); |
||
422 | } |
||
423 | } |
||
424 |