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 Composer\Command\BaseCommand; |
||
22 | use Limoncello\Commands\Traits\CacheFilePathTrait; |
||
23 | use Limoncello\Commands\Traits\CommandSerializationTrait; |
||
24 | use Limoncello\Commands\Traits\CommandTrait; |
||
25 | use Limoncello\Contracts\Application\ApplicationConfigurationInterface as S; |
||
26 | use Limoncello\Contracts\Application\CacheSettingsProviderInterface; |
||
27 | use Limoncello\Contracts\Commands\CommandInterface; |
||
28 | use Limoncello\Contracts\Commands\CommandStorageInterface; |
||
29 | use Limoncello\Contracts\Commands\IoInterface; |
||
30 | use Limoncello\Contracts\FileSystem\FileSystemInterface; |
||
31 | use Psr\Container\ContainerExceptionInterface; |
||
32 | use Psr\Container\ContainerInterface; |
||
33 | use Psr\Container\NotFoundExceptionInterface; |
||
34 | use ReflectionException; |
||
35 | use Symfony\Component\Console\Input\InputArgument; |
||
36 | use Symfony\Component\Console\Input\InputInterface; |
||
37 | use Symfony\Component\Console\Output\OutputInterface; |
||
38 | use function assert; |
||
39 | use function array_key_exists; |
||
40 | |||
41 | /** |
||
42 | * This is a special command which is immediately available from composer. The main purpose of it is to |
||
43 | * load command list from user application and generate a special cache file with the list. On the next |
||
44 | * composer run the list would be loaded into composer and all the commands would be available. |
||
45 | * |
||
46 | * Also it provides such a nice feature as generation of an empty/template command for the developer. |
||
47 | * |
||
48 | * @package Limoncello\Commands |
||
49 | * |
||
50 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
||
51 | */ |
||
52 | class CommandsCommand extends BaseCommand |
||
53 | { |
||
54 | use CommandTrait, CommandSerializationTrait, CacheFilePathTrait; |
||
55 | |||
56 | /** |
||
57 | * Command name. |
||
58 | */ |
||
59 | const NAME = 'l:commands'; |
||
60 | |||
61 | /** Argument name */ |
||
62 | const ARG_ACTION = 'action'; |
||
63 | |||
64 | /** Command action */ |
||
65 | const ACTION_CONNECT = 'connect'; |
||
66 | |||
67 | /** Command action */ |
||
68 | const ACTION_CREATE = 'create'; |
||
69 | |||
70 | /** Argument name */ |
||
71 | const ARG_CLASS = 'class'; |
||
72 | 9 | ||
73 | /** |
||
74 | 9 | * Constructor. |
|
75 | */ |
||
76 | public function __construct() |
||
77 | { |
||
78 | parent::__construct(static::NAME); |
||
79 | } |
||
80 | 9 | ||
81 | /** |
||
82 | 9 | * @inheritdoc |
|
83 | */ |
||
84 | 9 | public function configure() |
|
85 | 9 | { |
|
86 | 9 | parent::configure(); |
|
87 | 9 | ||
88 | $connect = static::ACTION_CONNECT; |
||
89 | 9 | $create = static::ACTION_CREATE; |
|
90 | $actionDesc = "Required action such as `$connect` to find and connect commands from application and plugins " . |
||
91 | "or `$create` to create an empty command template."; |
||
92 | 9 | ||
93 | 9 | $classDesc = "Required valid class name in commands' namespace for action `$create`."; |
|
94 | 9 | ||
95 | 9 | $this |
|
96 | 9 | ->setDescription('Manages commands executed from composer.') |
|
97 | ->setHelp('This command connects plugin and user-defined commands and creates new commands.') |
||
98 | ->setDefinition([ |
||
99 | new InputArgument(static::ARG_ACTION, InputArgument::REQUIRED, $actionDesc), |
||
100 | new InputArgument(static::ARG_CLASS, InputArgument::OPTIONAL, $classDesc), |
||
101 | ]); |
||
102 | } |
||
103 | |||
104 | |||
105 | /** @noinspection PhpMissingParentCallCommonInspection |
||
106 | 8 | * @inheritdoc |
|
107 | * |
||
108 | 8 | * @throws ReflectionException |
|
109 | 8 | */ |
|
110 | public function execute(InputInterface $input, OutputInterface $output) |
||
111 | 8 | { |
|
112 | 8 | $inOut = $this->wrapIo($input, $output); |
|
113 | $container = $this->createContainer($this->getComposer(), static::NAME); |
||
0 ignored issues
–
show
The call to
CommandsCommand::createContainer() has too many arguments starting with static::NAME .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
114 | 8 | ||
115 | 3 | $argAction = static::ARG_ACTION; |
|
116 | 3 | $action = $inOut->getArgument($argAction); |
|
117 | 5 | switch ($action) { |
|
118 | 4 | case static::ACTION_CONNECT: |
|
119 | 4 | $this->executeConnect($container, $inOut); |
|
120 | break; |
||
121 | 1 | case static::ACTION_CREATE: |
|
122 | 1 | $this->executeCreate($container, $inOut); |
|
123 | break; |
||
124 | default: |
||
125 | $inOut->writeError("Unknown value `$action` for argument `$argAction`." . PHP_EOL); |
||
126 | break; |
||
127 | } |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * @param ContainerInterface $container |
||
132 | * @param IoInterface $inOut |
||
133 | * |
||
134 | * @return void |
||
135 | 3 | * |
|
136 | * @throws ContainerExceptionInterface |
||
137 | 3 | * @throws NotFoundExceptionInterface |
|
138 | */ |
||
139 | 3 | private function executeConnect(ContainerInterface $container, IoInterface $inOut): void |
|
140 | { |
||
141 | 3 | assert($container->has(CommandStorageInterface::class)); |
|
142 | 3 | /** @var CommandStorageInterface $commandStorage */ |
|
143 | 3 | $commandStorage = $container->get(CommandStorageInterface::class); |
|
144 | 3 | ||
145 | $commandClasses = []; |
||
146 | 1 | foreach ($commandStorage->getAll() as $commandClass) { |
|
147 | 1 | if (class_exists($commandClass) === false || |
|
148 | array_key_exists(CommandInterface::class, class_implements($commandClass)) === false |
||
149 | ) { |
||
150 | 2 | $inOut->writeWarning("Class `$commandClass` either do not exist or not a command class." . PHP_EOL); |
|
151 | continue; |
||
152 | 2 | } |
|
153 | |||
154 | $inOut->writeInfo("Found command class `$commandClass`." . PHP_EOL, IoInterface::VERBOSITY_VERBOSE); |
||
155 | 3 | ||
156 | 2 | $commandClasses[] = $this->commandClassToArray($commandClass); |
|
157 | 2 | } |
|
158 | |||
159 | if (empty($commandClasses) === false) { |
||
160 | $now = date(DATE_RFC2822); |
||
161 | $data = var_export($commandClasses, true); |
||
162 | 2 | $content = <<<EOT |
|
163 | <?php |
||
164 | 2 | ||
165 | // THIS FILE IS AUTO GENERATED. DO NOT EDIT IT MANUALLY. |
||
166 | // Generated at: $now |
||
167 | 2 | ||
168 | 2 | return $data; |
|
169 | 1 | ||
170 | EOT; |
||
171 | 1 | $cacheFilePath = $this->getCommandsCacheFilePath($this->getComposer()); |
|
0 ignored issues
–
show
It seems like
$this->getComposer() can be null ; however, getCommandsCacheFilePath() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
172 | if (empty($cacheFilePath) === true) { |
||
173 | $inOut->writeError("Commands cache file path is not set. Check your `Application` settings." . PHP_EOL); |
||
174 | 1 | ||
175 | return; |
||
176 | 1 | } |
|
177 | |||
178 | 1 | $this->getFileSystem($container)->write($cacheFilePath, $content); |
|
179 | |||
180 | $inOut->writeInfo('Commands connected.' . PHP_EOL); |
||
181 | 1 | ||
182 | return; |
||
183 | } |
||
184 | |||
185 | $inOut->writeWarning('No commands found.' . PHP_EOL); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * @param ContainerInterface $container |
||
190 | * @param IoInterface $inOut |
||
191 | * |
||
192 | * @return void |
||
193 | 4 | * |
|
194 | * @throws ContainerExceptionInterface |
||
195 | 4 | * @throws NotFoundExceptionInterface |
|
196 | 4 | */ |
|
197 | 1 | private function executeCreate(ContainerInterface $container, IoInterface $inOut): void |
|
198 | { |
||
199 | 1 | $argClass = static::ARG_CLASS; |
|
200 | if ($inOut->hasArgument($argClass) === false) { |
||
201 | 3 | $inOut->writeError("Argument `$argClass` is not provided." . PHP_EOL); |
|
202 | |||
203 | 3 | return; |
|
204 | 3 | } |
|
205 | 3 | $class = $inOut->getArgument($argClass); |
|
206 | 1 | ||
207 | 1 | $fileSystem = $this->getFileSystem($container); |
|
208 | $commandsFolder = $this->getCommandsFolder($container); |
||
209 | if (empty($commandsFolder) === true || $fileSystem->isFolder($commandsFolder) === false) { |
||
210 | 1 | $inOut->writeError( |
|
211 | "Commands folder `$commandsFolder` is not valid. Check your `Application` settings." . PHP_EOL |
||
212 | ); |
||
213 | 2 | ||
214 | 2 | return; |
|
215 | 2 | } |
|
216 | |||
217 | 1 | $classPath = $commandsFolder . DIRECTORY_SEPARATOR . $class . '.php'; |
|
218 | 1 | if (ctype_alpha($class) === false || |
|
219 | 1 | $fileSystem->exists($classPath) === true |
|
220 | ) { |
||
221 | $inOut->writeError( |
||
222 | 1 | "Class name `$class` does not look valid for a command. " . |
|
223 | 'Can you please choose another one?' . PHP_EOL |
||
224 | ); |
||
225 | |||
226 | 1 | return; |
|
227 | 1 | } |
|
228 | 1 | ||
229 | $replace = function (string $template, iterable $parameters): string { |
||
230 | $result = $template; |
||
231 | 1 | foreach ($parameters as $key => $value) { |
|
232 | 1 | $result = str_replace($key, $value, $result); |
|
233 | } |
||
234 | 1 | ||
235 | 1 | return $result; |
|
236 | 1 | }; |
|
237 | 1 | ||
238 | 1 | $templateContent = $fileSystem->read(__DIR__ . DIRECTORY_SEPARATOR . 'SampleCommand.txt'); |
|
239 | $fileSystem->write($classPath, $replace($templateContent, [ |
||
240 | '{CLASS_NAME}' => $class, |
||
241 | '{COMMAND_NAME}' => strtolower($class), |
||
242 | '{TO_DO}' => 'TODO', |
||
243 | ])); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * @param ContainerInterface $container |
||
248 | * |
||
249 | * @return string |
||
250 | 3 | * |
|
251 | * @throws ContainerExceptionInterface |
||
252 | 3 | * @throws NotFoundExceptionInterface |
|
253 | */ |
||
254 | private function getCommandsFolder(ContainerInterface $container): string |
||
255 | 3 | { |
|
256 | 3 | assert($container->has(CacheSettingsProviderInterface::class)); |
|
257 | 3 | ||
258 | /** @var CacheSettingsProviderInterface $provider */ |
||
259 | 3 | $provider = $container->get(CacheSettingsProviderInterface::class); |
|
260 | $appConfig = $provider->getApplicationConfiguration(); |
||
261 | $folder = $appConfig[S::KEY_COMMANDS_FOLDER]; |
||
262 | |||
263 | return $folder; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * @param ContainerInterface $container |
||
268 | * |
||
269 | * @return FileSystemInterface |
||
270 | 4 | * |
|
271 | * @throws ContainerExceptionInterface |
||
272 | 4 | * @throws NotFoundExceptionInterface |
|
273 | */ |
||
274 | private function getFileSystem(ContainerInterface $container): FileSystemInterface |
||
275 | 4 | { |
|
276 | assert($container->has(FileSystemInterface::class)); |
||
277 | 4 | ||
278 | /** @var FileSystemInterface $fileSystem */ |
||
279 | $fileSystem = $container->get(FileSystemInterface::class); |
||
280 | |||
281 | return $fileSystem; |
||
282 | } |
||
283 | } |
||
284 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: