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.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php declare(strict_types=1); |
||
2 | /** |
||
3 | * Starlit App. |
||
4 | * |
||
5 | * @copyright Copyright (c) 2016 Starweb AB |
||
6 | * @license BSD 3-Clause |
||
7 | */ |
||
8 | |||
9 | namespace Starlit\App; |
||
10 | |||
11 | use Symfony\Component\HttpFoundation\Request; |
||
12 | use Symfony\Component\HttpFoundation\Response; |
||
13 | use Symfony\Component\Routing; |
||
14 | use Starlit\Utils\Str; |
||
15 | use Starlit\Utils\Url; |
||
16 | |||
17 | abstract class AbstractController |
||
18 | { |
||
19 | /** |
||
20 | * @var BaseApp |
||
21 | */ |
||
22 | protected $app; |
||
23 | |||
24 | /** |
||
25 | * @var Request |
||
26 | */ |
||
27 | protected $request; |
||
28 | |||
29 | /** |
||
30 | * @var View |
||
31 | */ |
||
32 | protected $view; |
||
33 | |||
34 | /** |
||
35 | * @var bool |
||
36 | */ |
||
37 | protected $autoRenderView = true; |
||
38 | |||
39 | /** |
||
40 | * @var string |
||
41 | */ |
||
42 | protected $autoRenderViewScript; |
||
43 | |||
44 | 29 | final public function __construct(BaseApp $app, Request $request) |
|
45 | { |
||
46 | 29 | $this->app = $app; |
|
47 | 29 | $this->request = $request; |
|
48 | |||
49 | 29 | $this->view = $this->app->getNew(ViewInterface::class); |
|
50 | 29 | $this->view->setRequest($this->request); |
|
51 | |||
52 | 29 | $this->init(); |
|
53 | 29 | } |
|
54 | |||
55 | /** |
||
56 | * Initialization method meant to be overridden in descendant classes (optional). |
||
57 | */ |
||
58 | 29 | protected function init() |
|
59 | { |
||
60 | 29 | } |
|
61 | |||
62 | /** |
||
63 | * Pre dispatch method meant to be overridden in descendant classes (optional). |
||
64 | * |
||
65 | * This method is called right before the actual action method is called/dispatched. |
||
66 | * Override this instead of init() if access to dispatch properties is required (like |
||
67 | * action name) or you need to return a response. |
||
68 | * |
||
69 | * @param string $action |
||
70 | * @return Response|null |
||
71 | */ |
||
72 | 7 | protected function preDispatch(string $action): ?Response |
|
73 | { |
||
74 | 7 | return null; |
|
75 | } |
||
76 | |||
77 | 2 | public function setAutoRenderView(bool $autoRenderView): void |
|
78 | { |
||
79 | 2 | $this->autoRenderView = $autoRenderView; |
|
80 | 2 | } |
|
81 | |||
82 | 1 | public function setAutoRenderViewScript(string $autoRenderViewScript): void |
|
83 | { |
||
84 | 1 | $this->autoRenderViewScript = $autoRenderViewScript; |
|
85 | 1 | } |
|
86 | |||
87 | /** |
||
88 | * Dispatch the requested action |
||
89 | * |
||
90 | * @param string|null $action action id/name (lowercase, - word separation) |
||
91 | * @param array $actionArgs |
||
92 | * @return Response |
||
93 | * @throws Routing\Exception\ResourceNotFoundException |
||
94 | */ |
||
95 | 11 | public function dispatch(string $action = null, array $actionArgs = []): Response |
|
96 | { |
||
97 | // If not special action is provided, try to get from request |
||
98 | 11 | $router = $this->app->get(RouterInterface::class); |
|
99 | 11 | $action = Str::camelToSeparator( |
|
100 | 11 | $action ?: $router->getRequestAction($this->request), |
|
101 | 11 | '-' |
|
102 | ); |
||
103 | 11 | $actionMethod = $router->getActionMethod($action); |
|
104 | 11 | $collectedArgs = $this->getCollectedDispatchArgs($actionMethod, $actionArgs); |
|
105 | |||
106 | // Call pre dispatch method and return it's response if there is one (uncommon) |
||
107 | 8 | $preDispatchResponse = $this->preDispatch($action); |
|
108 | 8 | if ($preDispatchResponse instanceof Response) { |
|
109 | 1 | return $preDispatchResponse; |
|
110 | } |
||
111 | |||
112 | // Call action method |
||
113 | 7 | $actionResponse = \call_user_func_array([$this, $actionMethod], $collectedArgs); |
|
114 | |||
115 | 7 | $this->postDispatch(); |
|
116 | |||
117 | 7 | return $this->getDispatchResponse($action, $actionResponse); |
|
118 | } |
||
119 | |||
120 | /** |
||
121 | * @throws Routing\Exception\ResourceNotFoundException |
||
122 | */ |
||
123 | 11 | protected function getCollectedDispatchArgs(string $actionMethod, array $actionArgs = []): array |
|
124 | { |
||
125 | // Check that method is a valid action method |
||
126 | try { |
||
127 | 11 | $reflectionMethod = new \ReflectionMethod($this, $actionMethod); |
|
128 | 10 | if (!$reflectionMethod->isPublic() || $reflectionMethod->isConstructor()) { |
|
129 | 1 | throw new Routing\Exception\ResourceNotFoundException( |
|
130 | 10 | "\"{$actionMethod}\" is not a valid action method." |
|
131 | ); |
||
132 | } |
||
133 | 2 | } catch (\ReflectionException $e) { |
|
134 | 1 | throw new Routing\Exception\ResourceNotFoundException("\"{$actionMethod}\" action method does not exist."); |
|
135 | } |
||
136 | |||
137 | 9 | $params = $reflectionMethod->getParameters(); |
|
138 | 9 | $predefinedValues = \array_merge($this->request->attributes->all() ?? [], $actionArgs); |
|
139 | |||
140 | try { |
||
141 | 9 | $collectedArgs = $this->app->resolveParameters($params, $predefinedValues); |
|
142 | 1 | } catch (\ReflectionException $e) { |
|
143 | 1 | throw new \LogicException('Missing values for one or more action parameters'); |
|
144 | } |
||
145 | |||
146 | 8 | return $collectedArgs; |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param string $action |
||
151 | * @param mixed $actionResponse |
||
152 | * @return Response |
||
153 | */ |
||
154 | 7 | protected function getDispatchResponse(string $action, $actionResponse): Response |
|
155 | { |
||
156 | 7 | if ($actionResponse instanceof Response) { |
|
157 | 4 | return $actionResponse->prepare($this->request); |
|
158 | 3 | } elseif ($actionResponse !== null) { |
|
159 | 1 | return $this->app->get(Response::class)->setContent((string) $actionResponse)->prepare($this->request); |
|
160 | 2 | } elseif ($this->autoRenderView) { |
|
161 | 1 | $viewScript = $this->autoRenderViewScript ?: $this->getAutoRenderViewScriptName( |
|
162 | 1 | $action, |
|
163 | 1 | $this->app->get(RouterInterface::class)->getRequestController($this->request), |
|
164 | 1 | $this->app->get(RouterInterface::class)->getRequestModule($this->request) |
|
165 | ); |
||
166 | |||
167 | 1 | return $this->app->get(Response::class)->setContent($this->view->render($viewScript, true)) |
|
168 | 1 | ->prepare($this->request); |
|
169 | } else { |
||
170 | // Empty response if no other response is set |
||
171 | 1 | return $this->app->get(Response::class)->setContent('') |
|
172 | 1 | ->prepare($this->request); |
|
173 | } |
||
174 | } |
||
175 | |||
176 | 1 | public function getAutoRenderViewScriptName(string $action, string $controller, string $module = null): string |
|
177 | { |
||
178 | 1 | $viewScriptName = \implode('/', \array_filter([$module, $controller, $action])); |
|
179 | |||
180 | 1 | return $viewScriptName; |
|
181 | } |
||
182 | |||
183 | /** |
||
184 | * Post dispatch method meant to be overridden in descendant classes (optional). |
||
185 | * This method is called right after an action method has returned it's response, |
||
186 | * but before the dispatch method returns the response. |
||
187 | */ |
||
188 | 7 | protected function postDispatch(): void |
|
189 | { |
||
190 | 7 | } |
|
191 | |||
192 | /** |
||
193 | * Forwards request to another action and/or controller |
||
194 | * |
||
195 | * @param string $action Action name as lowercase separated string |
||
196 | * @param string|null $controller Controller name as lowercase separated string |
||
197 | * @param string|null $module Module name as lowercase separated string |
||
198 | * @param array $actionArgs |
||
199 | * @return Response |
||
200 | */ |
||
201 | 3 | protected function forward( |
|
202 | string $action, |
||
203 | string $controller = null, |
||
204 | string $module = null, |
||
205 | array $actionArgs = [] |
||
206 | ): Response { |
||
207 | // Forward inside same controller (easy) |
||
208 | 3 | if (empty($controller)) { |
|
209 | 1 | return $this->dispatch($action, $actionArgs); |
|
210 | // Forward to another controller |
||
211 | } else { |
||
212 | 2 | $router = $this->app->get(RouterInterface::class); |
|
213 | 2 | $controller = $controller ?: $router->getRequestController($this->request); |
|
214 | 2 | $module = $module ?: $router->getRequestModule($this->request); |
|
215 | |||
216 | 2 | $controllerClass = $router->getControllerClass($controller, $module); |
|
217 | 2 | $actualController = new $controllerClass($this->app, $this->request); |
|
218 | |||
219 | // Set new request properties |
||
220 | 2 | $this->request->attributes->add(\compact('module', 'controller', 'action')); |
|
221 | |||
222 | 2 | return $actualController->dispatch($action, $actionArgs); |
|
223 | } |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Get current or a new url merged with provided parameters. |
||
228 | * |
||
229 | * @param string $relativeUrl |
||
230 | * @param array $parameters |
||
231 | * @return string |
||
232 | */ |
||
233 | 2 | protected function getUrl(string $relativeUrl = null, array $parameters = []): string |
|
234 | { |
||
235 | // Make an absolute url of a new one url, or use the current one if none is provided |
||
236 | 2 | if ($relativeUrl !== null) { |
|
237 | 1 | $url = $this->request->getSchemeAndHttpHost() . $relativeUrl; |
|
238 | } else { |
||
239 | 1 | $url = $this->request->getSchemeAndHttpHost() . $this->request->getRequestUri(); |
|
240 | } |
||
241 | |||
242 | 2 | if ($parameters) { |
|
0 ignored issues
–
show
|
|||
243 | 1 | $mergedParameters = \array_merge($this->get(), $parameters); |
|
244 | 1 | $url = (string) (new Url($url))->addQueryParameters($mergedParameters); |
|
245 | } |
||
246 | |||
247 | 2 | return $url; |
|
248 | } |
||
249 | |||
250 | /** |
||
251 | * Shortcut method to access GET/query parameters. |
||
252 | * |
||
253 | * @param string $key |
||
254 | * @param mixed $default |
||
255 | * @return mixed|null array if the key is null, otherwise the value for the key or its default value |
||
256 | * which is null by default |
||
257 | */ |
||
258 | 2 | protected function get(string $key = null, $default = null) |
|
259 | { |
||
260 | 2 | if ($key === null) { |
|
261 | 2 | return $this->request->query->all(); |
|
262 | } |
||
263 | |||
264 | 1 | return $this->request->query->get($key, $default); |
|
265 | } |
||
266 | |||
267 | /** |
||
268 | * Shortcut method to access POST/request parameters. |
||
269 | * |
||
270 | * @param string $key |
||
271 | * @param mixed $default |
||
272 | * @return string|array |
||
273 | */ |
||
274 | 1 | protected function post(string $key = null, $default = null) |
|
275 | { |
||
276 | 1 | if ($key === null) { |
|
277 | 1 | return $this->request->request->all(); |
|
278 | } |
||
279 | |||
280 | 1 | return $this->request->request->get($key, $default); |
|
281 | } |
||
282 | } |
||
283 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.