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 |
||
2 | namespace Consolidation\AnnotatedCommand\Parser; |
||
3 | |||
4 | use Symfony\Component\Console\Input\InputOption; |
||
5 | use Consolidation\AnnotatedCommand\Parser\Internal\CommandDocBlockParser; |
||
6 | use Consolidation\AnnotatedCommand\Parser\Internal\CommandDocBlockParserFactory; |
||
7 | use Consolidation\AnnotatedCommand\AnnotationData; |
||
8 | |||
9 | /** |
||
10 | * Given a class and method name, parse the annotations in the |
||
11 | * DocBlock comment, and provide accessor methods for all of |
||
12 | * the elements that are needed to create a Symfony Console Command. |
||
13 | * |
||
14 | * Note that the name of this class is now somewhat of a misnomer, |
||
15 | * as we now use it to hold annotation data for hooks as well as commands. |
||
16 | * It would probably be better to rename this to MethodInfo at some point. |
||
17 | */ |
||
18 | class CommandInfo |
||
19 | { |
||
20 | /** |
||
21 | * Serialization schema version. Incremented every time the serialization schema changes. |
||
22 | */ |
||
23 | const SERIALIZATION_SCHEMA_VERSION = 4; |
||
24 | |||
25 | /** |
||
26 | * @var \ReflectionMethod |
||
27 | */ |
||
28 | protected $reflection; |
||
29 | |||
30 | /** |
||
31 | * @var boolean |
||
32 | * @var string |
||
33 | */ |
||
34 | protected $docBlockIsParsed = false; |
||
35 | |||
36 | /** |
||
37 | * @var string |
||
38 | */ |
||
39 | protected $name; |
||
40 | |||
41 | /** |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $description = ''; |
||
45 | |||
46 | /** |
||
47 | * @var string |
||
48 | */ |
||
49 | protected $help = ''; |
||
50 | |||
51 | /** |
||
52 | * @var DefaultsWithDescriptions |
||
53 | */ |
||
54 | protected $options; |
||
55 | |||
56 | /** |
||
57 | * @var DefaultsWithDescriptions |
||
58 | */ |
||
59 | protected $arguments; |
||
60 | |||
61 | /** |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $exampleUsage = []; |
||
65 | |||
66 | /** |
||
67 | * @var AnnotationData |
||
68 | */ |
||
69 | protected $otherAnnotations; |
||
70 | |||
71 | /** |
||
72 | * @var array |
||
73 | */ |
||
74 | protected $aliases = []; |
||
75 | |||
76 | /** |
||
77 | * @var InputOption[] |
||
78 | */ |
||
79 | protected $inputOptions; |
||
80 | |||
81 | /** |
||
82 | * @var string |
||
83 | */ |
||
84 | protected $methodName; |
||
85 | |||
86 | /** |
||
87 | * @var string |
||
88 | */ |
||
89 | protected $returnType; |
||
90 | |||
91 | /** |
||
92 | * @var string[] |
||
93 | */ |
||
94 | protected $injectedClasses = []; |
||
95 | |||
96 | /** |
||
97 | * Create a new CommandInfo class for a particular method of a class. |
||
98 | * |
||
99 | * @param string|mixed $classNameOrInstance The name of a class, or an |
||
100 | * instance of it, or an array of cached data. |
||
101 | * @param string $methodName The name of the method to get info about. |
||
102 | * @param array $cache Cached data |
||
103 | * @deprecated Use CommandInfo::create() or CommandInfo::deserialize() |
||
104 | * instead. In the future, this constructor will be protected. |
||
105 | */ |
||
106 | public function __construct($classNameOrInstance, $methodName, $cache = []) |
||
107 | { |
||
108 | $this->reflection = new \ReflectionMethod($classNameOrInstance, $methodName); |
||
109 | $this->methodName = $methodName; |
||
110 | $this->arguments = new DefaultsWithDescriptions(); |
||
111 | $this->options = new DefaultsWithDescriptions(); |
||
112 | |||
113 | // If the cache came from a newer version, ignore it and |
||
114 | // regenerate the cached information. |
||
115 | if (!empty($cache) && CommandInfoDeserializer::isValidSerializedData($cache) && !$this->cachedFileIsModified($cache)) { |
||
116 | $deserializer = new CommandInfoDeserializer(); |
||
117 | $deserializer->constructFromCache($this, $cache); |
||
118 | $this->docBlockIsParsed = true; |
||
119 | } else { |
||
120 | $this->constructFromClassAndMethod($classNameOrInstance, $methodName); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | public static function create($classNameOrInstance, $methodName) |
||
125 | { |
||
126 | return new self($classNameOrInstance, $methodName); |
||
127 | } |
||
128 | |||
129 | public static function deserialize($cache) |
||
130 | { |
||
131 | $cache = (array)$cache; |
||
132 | return new self($cache['class'], $cache['method_name'], $cache); |
||
133 | } |
||
134 | |||
135 | public function cachedFileIsModified($cache) |
||
136 | { |
||
137 | $path = $this->reflection->getFileName(); |
||
138 | return filemtime($path) != $cache['mtime']; |
||
139 | } |
||
140 | |||
141 | protected function constructFromClassAndMethod($classNameOrInstance, $methodName) |
||
0 ignored issues
–
show
|
|||
142 | { |
||
143 | $this->otherAnnotations = new AnnotationData(); |
||
144 | // Set up a default name for the command from the method name. |
||
145 | // This can be overridden via @command or @name annotations. |
||
146 | $this->name = $this->convertName($methodName); |
||
147 | $this->options = new DefaultsWithDescriptions($this->determineOptionsFromParameters(), false); |
||
148 | $this->arguments = $this->determineAgumentClassifications(); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Recover the method name provided to the constructor. |
||
153 | * |
||
154 | * @return string |
||
155 | */ |
||
156 | public function getMethodName() |
||
157 | { |
||
158 | return $this->methodName; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Return the primary name for this command. |
||
163 | * |
||
164 | * @return string |
||
165 | */ |
||
166 | public function getName() |
||
167 | { |
||
168 | $this->parseDocBlock(); |
||
169 | return $this->name; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Set the primary name for this command. |
||
174 | * |
||
175 | * @param string $name |
||
176 | */ |
||
177 | public function setName($name) |
||
178 | { |
||
179 | $this->name = $name; |
||
180 | return $this; |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * Return whether or not this method represents a valid command |
||
185 | * or hook. |
||
186 | */ |
||
187 | public function valid() |
||
188 | { |
||
189 | return !empty($this->name); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * If higher-level code decides that this CommandInfo is not interesting |
||
194 | * or useful (if it is not a command method or a hook method), then |
||
195 | * we will mark it as invalid to prevent it from being created as a command. |
||
196 | * We still cache a placeholder record for invalid methods, so that we |
||
197 | * do not need to re-parse the method again later simply to determine that |
||
198 | * it is invalid. |
||
199 | */ |
||
200 | public function invalidate() |
||
201 | { |
||
202 | $this->name = ''; |
||
203 | } |
||
204 | |||
205 | public function getReturnType() |
||
206 | { |
||
207 | $this->parseDocBlock(); |
||
208 | return $this->returnType; |
||
209 | } |
||
210 | |||
211 | public function getInjectedClasses() |
||
212 | { |
||
213 | $this->parseDocBlock(); |
||
214 | return $this->injectedClasses; |
||
215 | } |
||
216 | |||
217 | public function setInjectedClasses($injectedClasses) |
||
218 | { |
||
219 | $this->injectedClasses = $injectedClasses; |
||
220 | return $this; |
||
221 | } |
||
222 | |||
223 | public function setReturnType($returnType) |
||
224 | { |
||
225 | $this->returnType = $returnType; |
||
226 | return $this; |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Get any annotations included in the docblock comment for the |
||
231 | * implementation method of this command that are not already |
||
232 | * handled by the primary methods of this class. |
||
233 | * |
||
234 | * @return AnnotationData |
||
235 | */ |
||
236 | public function getRawAnnotations() |
||
237 | { |
||
238 | $this->parseDocBlock(); |
||
239 | return $this->otherAnnotations; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Replace the annotation data. |
||
244 | */ |
||
245 | public function replaceRawAnnotations($annotationData) |
||
246 | { |
||
247 | $this->otherAnnotations = new AnnotationData((array) $annotationData); |
||
248 | return $this; |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Get any annotations included in the docblock comment, |
||
253 | * also including default values such as @command. We add |
||
254 | * in the default @command annotation late, and only in a |
||
255 | * copy of the annotation data because we use the existance |
||
256 | * of a @command to indicate that this CommandInfo is |
||
257 | * a command, and not a hook or anything else. |
||
258 | * |
||
259 | * @return AnnotationData |
||
260 | */ |
||
261 | public function getAnnotations() |
||
262 | { |
||
263 | // Also provide the path to the commandfile that these annotations |
||
264 | // were pulled from and the classname of that file. |
||
265 | $path = $this->reflection->getFileName(); |
||
266 | $className = $this->reflection->getDeclaringClass()->getName(); |
||
0 ignored issues
–
show
Consider using
$this->reflection->class . There is an issue with getName() and APC-enabled PHP versions.
![]() |
|||
267 | return new AnnotationData( |
||
268 | $this->getRawAnnotations()->getArrayCopy() + |
||
269 | [ |
||
270 | 'command' => $this->getName(), |
||
271 | '_path' => $path, |
||
272 | '_classname' => $className, |
||
273 | ] |
||
274 | ); |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Return a specific named annotation for this command as a list. |
||
279 | * |
||
280 | * @param string $name The name of the annotation. |
||
281 | * @return array|null |
||
282 | */ |
||
283 | public function getAnnotationList($name) |
||
284 | { |
||
285 | // hasAnnotation parses the docblock |
||
286 | if (!$this->hasAnnotation($name)) { |
||
287 | return null; |
||
288 | } |
||
289 | return $this->otherAnnotations->getList($name); |
||
290 | ; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Return a specific named annotation for this command as a string. |
||
295 | * |
||
296 | * @param string $name The name of the annotation. |
||
297 | * @return string|null |
||
298 | */ |
||
299 | public function getAnnotation($name) |
||
300 | { |
||
301 | // hasAnnotation parses the docblock |
||
302 | if (!$this->hasAnnotation($name)) { |
||
303 | return null; |
||
304 | } |
||
305 | return $this->otherAnnotations->get($name); |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * Check to see if the specified annotation exists for this command. |
||
310 | * |
||
311 | * @param string $annotation The name of the annotation. |
||
312 | * @return boolean |
||
313 | */ |
||
314 | public function hasAnnotation($annotation) |
||
315 | { |
||
316 | $this->parseDocBlock(); |
||
317 | return isset($this->otherAnnotations[$annotation]); |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Save any tag that we do not explicitly recognize in the |
||
322 | * 'otherAnnotations' map. |
||
323 | */ |
||
324 | public function addAnnotation($name, $content) |
||
325 | { |
||
326 | // Convert to an array and merge if there are multiple |
||
327 | // instances of the same annotation defined. |
||
328 | if (isset($this->otherAnnotations[$name])) { |
||
329 | $content = array_merge((array) $this->otherAnnotations[$name], (array)$content); |
||
330 | } |
||
331 | $this->otherAnnotations[$name] = $content; |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Remove an annotation that was previoudly set. |
||
336 | */ |
||
337 | public function removeAnnotation($name) |
||
338 | { |
||
339 | unset($this->otherAnnotations[$name]); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Get the synopsis of the command (~first line). |
||
344 | * |
||
345 | * @return string |
||
346 | */ |
||
347 | public function getDescription() |
||
348 | { |
||
349 | $this->parseDocBlock(); |
||
350 | return $this->description; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Set the command description. |
||
355 | * |
||
356 | * @param string $description The description to set. |
||
357 | */ |
||
358 | public function setDescription($description) |
||
359 | { |
||
360 | $this->description = str_replace("\n", ' ', $description); |
||
361 | return $this; |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Get the help text of the command (the description) |
||
366 | */ |
||
367 | public function getHelp() |
||
368 | { |
||
369 | $this->parseDocBlock(); |
||
370 | return $this->help; |
||
371 | } |
||
372 | /** |
||
373 | * Set the help text for this command. |
||
374 | * |
||
375 | * @param string $help The help text. |
||
376 | */ |
||
377 | public function setHelp($help) |
||
378 | { |
||
379 | $this->help = $help; |
||
380 | return $this; |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Return the list of aliases for this command. |
||
385 | * @return string[] |
||
386 | */ |
||
387 | public function getAliases() |
||
388 | { |
||
389 | $this->parseDocBlock(); |
||
390 | return $this->aliases; |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Set aliases that can be used in place of the command's primary name. |
||
395 | * |
||
396 | * @param string|string[] $aliases |
||
397 | */ |
||
398 | public function setAliases($aliases) |
||
399 | { |
||
400 | if (is_string($aliases)) { |
||
401 | $aliases = explode(',', static::convertListToCommaSeparated($aliases)); |
||
402 | } |
||
403 | $this->aliases = array_filter($aliases); |
||
404 | return $this; |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Get hidden status for the command. |
||
409 | * @return bool |
||
410 | */ |
||
411 | public function getHidden() |
||
412 | { |
||
413 | $this->parseDocBlock(); |
||
414 | return $this->hasAnnotation('hidden'); |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * Set hidden status. List command omits hidden commands. |
||
419 | * |
||
420 | * @param bool $hidden |
||
421 | */ |
||
422 | public function setHidden($hidden) |
||
423 | { |
||
424 | $this->hidden = $hidden; |
||
0 ignored issues
–
show
The property
hidden does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
425 | return $this; |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Return the examples for this command. This is @usage instead of |
||
430 | * @example because the later is defined by the phpdoc standard to |
||
431 | * be example method calls. |
||
432 | * |
||
433 | * @return string[] |
||
434 | */ |
||
435 | public function getExampleUsages() |
||
436 | { |
||
437 | $this->parseDocBlock(); |
||
438 | return $this->exampleUsage; |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * Add an example usage for this command. |
||
443 | * |
||
444 | * @param string $usage An example of the command, including the command |
||
445 | * name and all of its example arguments and options. |
||
446 | * @param string $description An explanation of what the example does. |
||
447 | */ |
||
448 | public function setExampleUsage($usage, $description) |
||
449 | { |
||
450 | $this->exampleUsage[$usage] = $description; |
||
451 | return $this; |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * Overwrite all example usages |
||
456 | */ |
||
457 | public function replaceExampleUsages($usages) |
||
458 | { |
||
459 | $this->exampleUsage = $usages; |
||
460 | return $this; |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Return the topics for this command. |
||
465 | * |
||
466 | * @return string[] |
||
467 | */ |
||
468 | public function getTopics() |
||
469 | { |
||
470 | if (!$this->hasAnnotation('topics')) { |
||
471 | return []; |
||
472 | } |
||
473 | $topics = $this->getAnnotation('topics'); |
||
474 | return explode(',', trim($topics)); |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * Return the list of refleaction parameters. |
||
479 | * |
||
480 | * @return ReflectionParameter[] |
||
481 | */ |
||
482 | public function getParameters() |
||
483 | { |
||
484 | return $this->reflection->getParameters(); |
||
485 | } |
||
486 | |||
487 | /** |
||
488 | * Descriptions of commandline arguements for this command. |
||
489 | * |
||
490 | * @return DefaultsWithDescriptions |
||
491 | */ |
||
492 | public function arguments() |
||
493 | { |
||
494 | return $this->arguments; |
||
495 | } |
||
496 | |||
497 | /** |
||
498 | * Descriptions of commandline options for this command. |
||
499 | * |
||
500 | * @return DefaultsWithDescriptions |
||
501 | */ |
||
502 | public function options() |
||
503 | { |
||
504 | return $this->options; |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * Get the inputOptions for the options associated with this CommandInfo |
||
509 | * object, e.g. via @option annotations, or from |
||
510 | * $options = ['someoption' => 'defaultvalue'] in the command method |
||
511 | * parameter list. |
||
512 | * |
||
513 | * @return InputOption[] |
||
514 | */ |
||
515 | public function inputOptions() |
||
516 | { |
||
517 | if (!isset($this->inputOptions)) { |
||
518 | $this->inputOptions = $this->createInputOptions(); |
||
519 | } |
||
520 | return $this->inputOptions; |
||
521 | } |
||
522 | |||
523 | protected function addImplicitNoOptions() |
||
524 | { |
||
525 | $opts = $this->options()->getValues(); |
||
526 | foreach ($opts as $name => $defaultValue) { |
||
527 | if ($defaultValue === true) { |
||
528 | $key = 'no-' . $name; |
||
529 | if (!array_key_exists($key, $opts)) { |
||
530 | $description = "Negate --$name option."; |
||
531 | $this->options()->add($key, $description, false); |
||
532 | } |
||
533 | } |
||
534 | } |
||
535 | } |
||
536 | |||
537 | protected function createInputOptions() |
||
538 | { |
||
539 | $explicitOptions = []; |
||
540 | $this->addImplicitNoOptions(); |
||
541 | |||
542 | $opts = $this->options()->getValues(); |
||
543 | foreach ($opts as $name => $defaultValue) { |
||
544 | $description = $this->options()->getDescription($name); |
||
545 | |||
546 | $fullName = $name; |
||
547 | $shortcut = ''; |
||
548 | if (strpos($name, '|')) { |
||
549 | list($fullName, $shortcut) = explode('|', $name, 2); |
||
550 | } |
||
551 | |||
552 | // Treat the following two cases identically: |
||
553 | // - 'foo' => InputOption::VALUE_OPTIONAL |
||
554 | // - 'foo' => null |
||
555 | // The first form is preferred, but we will convert the value |
||
556 | // to 'null' for storage as the option default value. |
||
557 | if ($defaultValue === InputOption::VALUE_OPTIONAL) { |
||
558 | $defaultValue = null; |
||
559 | } |
||
560 | |||
561 | if ($defaultValue === false) { |
||
562 | $explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_NONE, $description); |
||
563 | } elseif ($defaultValue === InputOption::VALUE_REQUIRED) { |
||
564 | $explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_REQUIRED, $description); |
||
565 | } elseif (is_array($defaultValue)) { |
||
566 | $optionality = count($defaultValue) ? InputOption::VALUE_OPTIONAL : InputOption::VALUE_REQUIRED; |
||
567 | $explicitOptions[$fullName] = new InputOption( |
||
568 | $fullName, |
||
569 | $shortcut, |
||
570 | InputOption::VALUE_IS_ARRAY | $optionality, |
||
571 | $description, |
||
572 | count($defaultValue) ? $defaultValue : null |
||
0 ignored issues
–
show
It seems like
count($defaultValue) ? $defaultValue : null can also be of type array ; however, Symfony\Component\Consol...utOption::__construct() does only seem to accept string|array<integer,string>|integer|boolean|null , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
573 | ); |
||
574 | } else { |
||
575 | $explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_OPTIONAL, $description, $defaultValue); |
||
576 | } |
||
577 | } |
||
578 | |||
579 | return $explicitOptions; |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * An option might have a name such as 'silent|s'. In this |
||
584 | * instance, we will allow the @option or @default tag to |
||
585 | * reference the option only by name (e.g. 'silent' or 's' |
||
586 | * instead of 'silent|s'). |
||
587 | * |
||
588 | * @param string $optionName |
||
589 | * @return string |
||
590 | */ |
||
591 | public function findMatchingOption($optionName) |
||
592 | { |
||
593 | // Exit fast if there's an exact match |
||
594 | if ($this->options->exists($optionName)) { |
||
595 | return $optionName; |
||
596 | } |
||
597 | $existingOptionName = $this->findExistingOption($optionName); |
||
598 | if (isset($existingOptionName)) { |
||
599 | return $existingOptionName; |
||
600 | } |
||
601 | return $this->findOptionAmongAlternatives($optionName); |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * @param string $optionName |
||
606 | * @return string |
||
607 | */ |
||
608 | protected function findOptionAmongAlternatives($optionName) |
||
609 | { |
||
610 | // Check the other direction: if the annotation contains @silent|s |
||
611 | // and the options array has 'silent|s'. |
||
612 | $checkMatching = explode('|', $optionName); |
||
613 | if (count($checkMatching) > 1) { |
||
614 | foreach ($checkMatching as $checkName) { |
||
615 | if ($this->options->exists($checkName)) { |
||
616 | $this->options->rename($checkName, $optionName); |
||
617 | return $optionName; |
||
618 | } |
||
619 | } |
||
620 | } |
||
621 | return $optionName; |
||
622 | } |
||
623 | |||
624 | /** |
||
625 | * @param string $optionName |
||
626 | * @return string|null |
||
627 | */ |
||
628 | protected function findExistingOption($optionName) |
||
629 | { |
||
630 | // Check to see if we can find the option name in an existing option, |
||
631 | // e.g. if the options array has 'silent|s' => false, and the annotation |
||
632 | // is @silent. |
||
633 | foreach ($this->options()->getValues() as $name => $default) { |
||
634 | if (in_array($optionName, explode('|', $name))) { |
||
635 | return $name; |
||
636 | } |
||
637 | } |
||
638 | } |
||
639 | |||
640 | /** |
||
641 | * Examine the parameters of the method for this command, and |
||
642 | * build a list of commandline arguements for them. |
||
643 | * |
||
644 | * @return array |
||
645 | */ |
||
646 | protected function determineAgumentClassifications() |
||
647 | { |
||
648 | $result = new DefaultsWithDescriptions(); |
||
649 | $params = $this->reflection->getParameters(); |
||
650 | $optionsFromParameters = $this->determineOptionsFromParameters(); |
||
0 ignored issues
–
show
$optionsFromParameters is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
651 | if ($this->lastParameterIsOptionsArray()) { |
||
652 | array_pop($params); |
||
653 | } |
||
654 | while (!empty($params) && ($params[0]->getClass() != null)) { |
||
655 | $param = array_shift($params); |
||
656 | $injectedClass = $param->getClass()->getName(); |
||
657 | array_unshift($this->injectedClasses, $injectedClass); |
||
658 | } |
||
659 | foreach ($params as $param) { |
||
660 | $this->addParameterToResult($result, $param); |
||
661 | } |
||
662 | return $result; |
||
663 | } |
||
664 | |||
665 | /** |
||
666 | * Examine the provided parameter, and determine whether it |
||
667 | * is a parameter that will be filled in with a positional |
||
668 | * commandline argument. |
||
669 | */ |
||
670 | protected function addParameterToResult($result, $param) |
||
671 | { |
||
672 | // Commandline arguments must be strings, so ignore any |
||
673 | // parameter that is typehinted to any non-primative class. |
||
674 | if ($param->getClass() != null) { |
||
675 | return; |
||
676 | } |
||
677 | $result->add($param->name); |
||
678 | if ($param->isDefaultValueAvailable()) { |
||
679 | $defaultValue = $param->getDefaultValue(); |
||
680 | if (!$this->isAssoc($defaultValue)) { |
||
681 | $result->setDefaultValue($param->name, $defaultValue); |
||
682 | } |
||
683 | } elseif ($param->isArray()) { |
||
684 | $result->setDefaultValue($param->name, []); |
||
685 | } |
||
686 | } |
||
687 | |||
688 | /** |
||
689 | * Examine the parameters of the method for this command, and determine |
||
690 | * the disposition of the options from them. |
||
691 | * |
||
692 | * @return array |
||
693 | */ |
||
694 | protected function determineOptionsFromParameters() |
||
695 | { |
||
696 | $params = $this->reflection->getParameters(); |
||
697 | if (empty($params)) { |
||
698 | return []; |
||
699 | } |
||
700 | $param = end($params); |
||
701 | if (!$param->isDefaultValueAvailable()) { |
||
702 | return []; |
||
703 | } |
||
704 | if (!$this->isAssoc($param->getDefaultValue())) { |
||
705 | return []; |
||
706 | } |
||
707 | return $param->getDefaultValue(); |
||
708 | } |
||
709 | |||
710 | /** |
||
711 | * Determine if the last argument contains $options. |
||
712 | * |
||
713 | * Two forms indicate options: |
||
714 | * - $options = [] |
||
715 | * - $options = ['flag' => 'default-value'] |
||
716 | * |
||
717 | * Any other form, including `array $foo`, is not options. |
||
718 | */ |
||
719 | protected function lastParameterIsOptionsArray() |
||
720 | { |
||
721 | $params = $this->reflection->getParameters(); |
||
722 | if (empty($params)) { |
||
723 | return []; |
||
724 | } |
||
725 | $param = end($params); |
||
726 | if (!$param->isDefaultValueAvailable()) { |
||
727 | return []; |
||
728 | } |
||
729 | return is_array($param->getDefaultValue()); |
||
730 | } |
||
731 | |||
732 | /** |
||
733 | * Helper; determine if an array is associative or not. An array |
||
734 | * is not associative if its keys are numeric, and numbered sequentially |
||
735 | * from zero. All other arrays are considered to be associative. |
||
736 | * |
||
737 | * @param array $arr The array |
||
738 | * @return boolean |
||
739 | */ |
||
740 | protected function isAssoc($arr) |
||
741 | { |
||
742 | if (!is_array($arr)) { |
||
743 | return false; |
||
744 | } |
||
745 | return array_keys($arr) !== range(0, count($arr) - 1); |
||
746 | } |
||
747 | |||
748 | /** |
||
749 | * Convert from a method name to the corresponding command name. A |
||
750 | * method 'fooBar' will become 'foo:bar', and 'fooBarBazBoz' will |
||
751 | * become 'foo:bar-baz-boz'. |
||
752 | * |
||
753 | * @param string $camel method name. |
||
754 | * @return string |
||
755 | */ |
||
756 | protected function convertName($camel) |
||
757 | { |
||
758 | $splitter="-"; |
||
759 | $camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel)); |
||
760 | $camel = preg_replace("/$splitter/", ':', $camel, 1); |
||
761 | return strtolower($camel); |
||
762 | } |
||
763 | |||
764 | /** |
||
765 | * Parse the docBlock comment for this command, and set the |
||
766 | * fields of this class with the data thereby obtained. |
||
767 | */ |
||
768 | protected function parseDocBlock() |
||
769 | { |
||
770 | if (!$this->docBlockIsParsed) { |
||
771 | // The parse function will insert data from the provided method |
||
772 | // into this object, using our accessors. |
||
773 | CommandDocBlockParserFactory::parse($this, $this->reflection); |
||
774 | $this->docBlockIsParsed = true; |
||
775 | } |
||
776 | } |
||
777 | |||
778 | /** |
||
779 | * Given a list that might be 'a b c' or 'a, b, c' or 'a,b,c', |
||
780 | * convert the data into the last of these forms. |
||
781 | */ |
||
782 | protected static function convertListToCommaSeparated($text) |
||
783 | { |
||
784 | return preg_replace('#[ \t\n\r,]+#', ',', $text); |
||
785 | } |
||
786 | } |
||
787 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.