<?php
namespace Consolidation\AnnotatedCommand;
+use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
protected $usesOutputInterface;
/** var boolean */
protected $includeOptionsInArgs;
+ /** var array */
+ protected $specialDefaults = [];
public function __construct(
AnnotationData $annotationData,
public function options()
{
- return $this->input->getOptions();
+ // We cannot tell the difference between '--foo' (an option without
+ // a value) and the absence of '--foo' when the option has an optional
+ // value, and the current vallue of the option is 'null' using only
+ // the public methods of InputInterface. We'll try to figure out
+ // which is which by other means here.
+ $options = $this->getAdjustedOptions();
+
+ // Make two conversions here:
+ // --foo=0 wil convert $value from '0' to 'false' for binary options.
+ // --foo with $value of 'true' will be forced to 'false' if --no-foo exists.
+ foreach ($options as $option => $value) {
+ if ($this->shouldConvertOptionToFalse($options, $option, $value)) {
+ $options[$option] = false;
+ }
+ }
+
+ return $options;
+ }
+
+ /**
+ * Use 'hasParameterOption()' to attempt to disambiguate option states.
+ */
+ protected function getAdjustedOptions()
+ {
+ $options = $this->input->getOptions();
+
+ // If Input isn't an ArgvInput, then return the options as-is.
+ if (!$this->input instanceof ArgvInput) {
+ return $options;
+ }
+
+ // If we have an ArgvInput, then we can determine if options
+ // are missing from the command line. If the option value is
+ // missing from $input, then we will keep the value `null`.
+ // If it is present, but has no explicit value, then change it its
+ // value to `true`.
+ foreach ($options as $option => $value) {
+ if (($value === null) && ($this->input->hasParameterOption("--$option"))) {
+ $options[$option] = true;
+ }
+ }
+
+ return $options;
+ }
+
+ protected function shouldConvertOptionToFalse($options, $option, $value)
+ {
+ // If the value is 'true' (e.g. the option is '--foo'), then convert
+ // it to false if there is also an option '--no-foo'. n.b. if the
+ // commandline has '--foo=bar' then $value will not be 'true', and
+ // --no-foo will be ignored.
+ if ($value === true) {
+ // Check if the --no-* option exists. Note that none of the other
+ // alteration apply in the $value == true case, so we can exit early here.
+ $negation_key = 'no-' . $option;
+ return array_key_exists($negation_key, $options) && $options[$negation_key];
+ }
+
+ // If the option is '--foo=0', convert the '0' to 'false' when appropriate.
+ if ($value !== '0') {
+ return false;
+ }
+
+ // The '--foo=0' convertion is only applicable when the default value
+ // is not in the special defaults list. i.e. you get a literal '0'
+ // when your default is a string.
+ return in_array($option, $this->specialDefaults);
+ }
+
+ public function cacheSpecialDefaults($definition)
+ {
+ foreach ($definition->getOptions() as $option => $inputOption) {
+ $defaultValue = $inputOption->getDefault();
+ if (($defaultValue === null) || ($defaultValue === true)) {
+ $this->specialDefaults[] = $option;
+ }
+ }
}
public function getArgsWithoutAppName()
// will be the command name. The Application alters the
// input definition to match, adding a 'command' argument
// to the beginning.
- array_shift($args);
-
- if ($this->usesInputInterface) {
- array_unshift($args, $this->input());
+ if ($this->input->hasArgument('command')) {
+ array_shift($args);
}
if ($this->usesOutputInterface) {
array_unshift($args, $this->output());
}
+ if ($this->usesInputInterface) {
+ array_unshift($args, $this->input());
+ }
+
return $args;
}