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 | namespace Limoncello\Commands; |
||
4 | |||
5 | /** |
||
6 | * Copyright 2015-2019 [email protected] |
||
7 | * |
||
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
9 | * you may not use this file except in compliance with the License. |
||
10 | * You may obtain a copy of the License at |
||
11 | * |
||
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
13 | * |
||
14 | * Unless required by applicable law or agreed to in writing, software |
||
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
17 | * See the License for the specific language governing permissions and |
||
18 | * limitations under the License. |
||
19 | */ |
||
20 | |||
21 | use Closure; |
||
22 | use Limoncello\Common\Reflection\CheckCallableTrait; |
||
23 | use Limoncello\Common\Reflection\ClassIsTrait; |
||
24 | use Limoncello\Contracts\Application\ApplicationConfigurationInterface; |
||
25 | use Limoncello\Contracts\Application\CacheSettingsProviderInterface; |
||
26 | use Limoncello\Contracts\Commands\IoInterface; |
||
27 | use Limoncello\Contracts\Commands\RoutesConfiguratorInterface; |
||
28 | use Limoncello\Contracts\Commands\RoutesInterface; |
||
29 | use Limoncello\Contracts\Container\ContainerInterface as LimoncelloContainerInterface; |
||
30 | use Limoncello\Contracts\FileSystem\FileSystemInterface; |
||
31 | use Psr\Container\ContainerInterface as PsrContainerInterface; |
||
32 | use ReflectionException; |
||
33 | use function assert; |
||
34 | use function array_merge; |
||
35 | use function call_user_func; |
||
36 | use function count; |
||
37 | |||
38 | /** |
||
39 | * Code for command execution is separated from the main code to get rid of a dependency from Composer. |
||
40 | * This code could be executed independently in tests without composer dependency. |
||
41 | * |
||
42 | * @package Limoncello\Commands |
||
43 | * |
||
44 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
||
45 | */ |
||
46 | trait ExecuteCommandTrait |
||
47 | { |
||
48 | use ClassIsTrait; |
||
49 | |||
50 | /** |
||
51 | * @param string $name |
||
52 | * @param callable $handler |
||
53 | * @param IoInterface $inOut |
||
54 | * @param LimoncelloContainerInterface $container |
||
55 | * |
||
56 | 2 | * @return void |
|
57 | * |
||
58 | * @throws ReflectionException |
||
59 | * |
||
60 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
61 | */ |
||
62 | public function executeCommand( |
||
63 | string $name, |
||
64 | callable $handler, |
||
65 | IoInterface $inOut, |
||
66 | LimoncelloContainerInterface $container |
||
67 | ): void { |
||
68 | // This method does bootstrap for every command (e.g. configure containers) |
||
69 | // and then calls the actual command handler. |
||
70 | |||
71 | 2 | // At this point we have probably only partly configured container and we need to read from it |
|
72 | 2 | // CLI route setting in order to fully configure it and then run the command with middleware. |
|
73 | 2 | // However, when we read anything from it, it changes its state so we are not allowed to add |
|
74 | // anything to it (technically we can but in some cases it might cause an exception). |
||
75 | // So, what's the solution? We clone the container, read from the clone everything we need, |
||
76 | 2 | // and then continue with the original unchanged container. |
|
77 | 2 | $routesPath = null; |
|
78 | if (true) { |
||
79 | 2 | $containerClone = clone $container; |
|
80 | 2 | ||
81 | /** @var CacheSettingsProviderInterface $provider */ |
||
82 | $provider = $container->get(CacheSettingsProviderInterface::class); |
||
83 | 2 | $appConfig = $provider->getApplicationConfiguration(); |
|
84 | 2 | ||
85 | 2 | $routesFolder = $appConfig[ApplicationConfigurationInterface::KEY_ROUTES_FOLDER] ?? ''; |
|
86 | 2 | $routesMask = $appConfig[ApplicationConfigurationInterface::KEY_ROUTES_FILE_MASK] ?? ''; |
|
87 | 2 | ||
88 | /** @var FileSystemInterface $files */ |
||
89 | assert( |
||
90 | 2 | ($files = $containerClone->get(FileSystemInterface::class)) !== null && |
|
91 | empty($routesFolder) === false && empty($routesMask) === false && |
||
92 | 2 | $files->exists($routesFolder) === true, |
|
93 | 'Routes folder and mask must be defined in application settings.' |
||
94 | ); |
||
95 | |||
96 | 2 | unset($containerClone); |
|
97 | |||
98 | 2 | $routesPath = $routesFolder . DIRECTORY_SEPARATOR . $routesMask; |
|
99 | } |
||
100 | 2 | ||
101 | [$configurators, $middleware] |
||
0 ignored issues
–
show
|
|||
102 | = $this->readExtraContainerConfiguratorsAndMiddleware($routesPath, $name); |
||
103 | |||
104 | 2 | $this->executeContainerConfigurators($configurators, $container); |
|
105 | |||
106 | $handler = $this->buildExecutionChain($middleware, $handler, $container); |
||
107 | |||
108 | // finally go through all middleware and execute command handler |
||
109 | // (container has to be the same (do not send as param), but middleware my wrap IO (send as param)). |
||
110 | call_user_func($handler, $inOut); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @param string $routesFolder |
||
115 | * @param string $commandName |
||
116 | * |
||
117 | 2 | * @return array |
|
118 | * |
||
119 | * @throws ReflectionException |
||
120 | * |
||
121 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |
||
122 | */ |
||
123 | private function readExtraContainerConfiguratorsAndMiddleware(string $routesFolder, string $commandName): array |
||
124 | { |
||
125 | $routesFilter = new class ($commandName) implements RoutesInterface |
||
126 | { |
||
127 | use CheckCallableTrait; |
||
128 | |||
129 | /** @var array */ |
||
130 | private $middleware = []; |
||
131 | |||
132 | /** @var array */ |
||
133 | private $configurators = []; |
||
134 | |||
135 | /** @var string */ |
||
136 | private $commandName; |
||
137 | 2 | ||
138 | /** |
||
139 | * @param string $commandName |
||
140 | */ |
||
141 | public function __construct(string $commandName) |
||
142 | { |
||
143 | $this->commandName = $commandName; |
||
144 | } |
||
145 | 2 | ||
146 | /** |
||
147 | 2 | * @inheritdoc |
|
148 | */ |
||
149 | 2 | public function addGlobalMiddleware(array $middleware): RoutesInterface |
|
150 | { |
||
151 | assert($this->checkMiddlewareCallables($middleware) === true); |
||
152 | |||
153 | $this->middleware = array_merge($this->middleware, $middleware); |
||
154 | |||
155 | return $this; |
||
156 | } |
||
157 | 2 | ||
158 | /** |
||
159 | 2 | * @inheritdoc |
|
160 | */ |
||
161 | 2 | public function addGlobalContainerConfigurators(array $configurators): RoutesInterface |
|
162 | { |
||
163 | assert($this->checkConfiguratorCallables($configurators) === true); |
||
164 | |||
165 | $this->configurators = array_merge($this->configurators, $configurators); |
||
166 | |||
167 | return $this; |
||
168 | } |
||
169 | 2 | ||
170 | /** |
||
171 | 2 | * @inheritdoc |
|
172 | 1 | */ |
|
173 | public function addCommandMiddleware(string $name, array $middleware): RoutesInterface |
||
174 | { |
||
175 | 2 | assert($this->checkMiddlewareCallables($middleware) === true); |
|
176 | |||
177 | if ($this->commandName === $name) { |
||
178 | $this->middleware = array_merge($this->middleware, $middleware); |
||
179 | } |
||
180 | |||
181 | return $this; |
||
182 | } |
||
183 | 2 | ||
184 | /** |
||
185 | 2 | * @inheritdoc |
|
186 | 1 | */ |
|
187 | public function addCommandContainerConfigurators(string $name, array $configurators): RoutesInterface |
||
188 | { |
||
189 | 2 | assert($this->checkConfiguratorCallables($configurators) === true); |
|
190 | |||
191 | if ($this->commandName === $name) { |
||
192 | $this->configurators = array_merge($this->configurators, $configurators); |
||
193 | } |
||
194 | |||
195 | return $this; |
||
196 | } |
||
197 | 2 | ||
198 | /** |
||
199 | * @return array |
||
200 | */ |
||
201 | public function getMiddleware(): array |
||
202 | { |
||
203 | return $this->middleware; |
||
204 | } |
||
205 | 2 | ||
206 | /** |
||
207 | * @return array |
||
208 | */ |
||
209 | public function getConfigurators(): array |
||
210 | { |
||
211 | return $this->configurators; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | 2 | * @param array $mightBeConfigurators |
|
216 | * |
||
217 | 2 | * @return bool |
|
218 | 2 | */ |
|
219 | 2 | private function checkConfiguratorCallables(array $mightBeConfigurators): bool |
|
220 | 2 | { |
|
221 | 2 | $result = true; |
|
222 | 2 | ||
223 | foreach ($mightBeConfigurators as $mightBeCallable) { |
||
224 | $result = $result === true && |
||
225 | $this->checkPublicStaticCallable( |
||
226 | 2 | $mightBeCallable, |
|
227 | [LimoncelloContainerInterface::class], |
||
228 | 'void' |
||
229 | ); |
||
230 | } |
||
231 | |||
232 | return $result; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | 2 | * @param array $mightBeMiddleware |
|
237 | * |
||
238 | 2 | * @return bool |
|
239 | 2 | */ |
|
240 | 2 | private function checkMiddlewareCallables(array $mightBeMiddleware): bool |
|
241 | 2 | { |
|
242 | 2 | $result = true; |
|
243 | |||
244 | foreach ($mightBeMiddleware as $mightBeCallable) { |
||
245 | $result = $result === true && $this->checkPublicStaticCallable( |
||
246 | 2 | $mightBeCallable, |
|
247 | [IoInterface::class, Closure::class, PsrContainerInterface::class], |
||
248 | 'void' |
||
249 | ); |
||
250 | 2 | } |
|
251 | |||
252 | 2 | return $result; |
|
253 | } |
||
254 | }; |
||
255 | 2 | ||
256 | foreach (static::selectClasses($routesFolder, RoutesConfiguratorInterface::class) as $class) { |
||
257 | /** @var RoutesConfiguratorInterface $class */ |
||
258 | $class::configureRoutes($routesFilter); |
||
259 | } |
||
260 | |||
261 | return [$routesFilter->getConfigurators(), $routesFilter->getMiddleware()]; |
||
262 | } |
||
263 | |||
264 | 2 | /** |
|
265 | * @param callable[] $configurators |
||
266 | 2 | * @param LimoncelloContainerInterface $container |
|
267 | 2 | * |
|
268 | * @return void |
||
269 | */ |
||
270 | private function executeContainerConfigurators(array $configurators, LimoncelloContainerInterface $container): void |
||
271 | { |
||
272 | foreach ($configurators as $configurator) { |
||
273 | call_user_func($configurator, $container); |
||
274 | } |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | 2 | * @param array $middleware |
|
279 | * @param callable $command |
||
280 | * @param PsrContainerInterface $container |
||
281 | * |
||
282 | * @return Closure |
||
283 | */ |
||
284 | 2 | private function buildExecutionChain( |
|
285 | 2 | array $middleware, |
|
286 | callable $command, |
||
287 | 2 | PsrContainerInterface $container |
|
288 | 2 | ): Closure { |
|
289 | $next = function (IoInterface $inOut) use ($command, $container): void { |
||
290 | 2 | call_user_func($command, $container, $inOut); |
|
291 | 2 | }; |
|
292 | |||
293 | for ($index = count($middleware) - 1; $index >= 0; $index--) { |
||
294 | 2 | $currentMiddleware = $middleware[$index]; |
|
295 | $next = function (IoInterface $inOut) use ($currentMiddleware, $next, $container): void { |
||
296 | call_user_func($currentMiddleware, $inOut, $next, $container); |
||
297 | }; |
||
298 | } |
||
299 | |||
300 | return $next; |
||
301 | } |
||
302 | } |
||
303 |
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.