2 namespace Consolidation\AnnotatedCommand;
4 use Symfony\Component\Console\Input\ArgvInput;
5 use Symfony\Component\Console\Input\InputInterface;
6 use Symfony\Component\Console\Output\OutputInterface;
10 /** var AnnotationData */
11 protected $annotationData;
12 /** var InputInterface */
14 /** var OutputInterface */
17 protected $usesInputInterface;
19 protected $usesOutputInterface;
21 protected $includeOptionsInArgs;
23 protected $specialDefaults = [];
25 public function __construct(
26 AnnotationData $annotationData,
27 InputInterface $input,
28 OutputInterface $output,
29 $usesInputInterface = false,
30 $usesOutputInterface = false
32 $this->annotationData = $annotationData;
33 $this->input = $input;
34 $this->output = $output;
35 $this->usesInputInterface = false;
36 $this->usesOutputInterface = false;
37 $this->includeOptionsInArgs = true;
41 * For internal use only; indicates that the function to be called
42 * should be passed an InputInterface &/or an OutputInterface.
43 * @param booean $usesInputInterface
44 * @param boolean $usesOutputInterface
47 public function setUseIOInterfaces($usesInputInterface, $usesOutputInterface)
49 $this->usesInputInterface = $usesInputInterface;
50 $this->usesOutputInterface = $usesOutputInterface;
55 * For backwards-compatibility mode only: disable addition of
56 * options on the end of the arguments list.
58 public function setIncludeOptionsInArgs($includeOptionsInArgs)
60 $this->includeOptionsInArgs = $includeOptionsInArgs;
64 public function annotationData()
66 return $this->annotationData;
69 public function input()
74 public function output()
79 public function arguments()
81 return $this->input->getArguments();
84 public function options()
86 // We cannot tell the difference between '--foo' (an option without
87 // a value) and the absence of '--foo' when the option has an optional
88 // value, and the current vallue of the option is 'null' using only
89 // the public methods of InputInterface. We'll try to figure out
90 // which is which by other means here.
91 $options = $this->getAdjustedOptions();
93 // Make two conversions here:
94 // --foo=0 wil convert $value from '0' to 'false' for binary options.
95 // --foo with $value of 'true' will be forced to 'false' if --no-foo exists.
96 foreach ($options as $option => $value) {
97 if ($this->shouldConvertOptionToFalse($options, $option, $value)) {
98 $options[$option] = false;
106 * Use 'hasParameterOption()' to attempt to disambiguate option states.
108 protected function getAdjustedOptions()
110 $options = $this->input->getOptions();
112 // If Input isn't an ArgvInput, then return the options as-is.
113 if (!$this->input instanceof ArgvInput) {
117 // If we have an ArgvInput, then we can determine if options
118 // are missing from the command line. If the option value is
119 // missing from $input, then we will keep the value `null`.
120 // If it is present, but has no explicit value, then change it its
122 foreach ($options as $option => $value) {
123 if (($value === null) && ($this->input->hasParameterOption("--$option"))) {
124 $options[$option] = true;
131 protected function shouldConvertOptionToFalse($options, $option, $value)
133 // If the value is 'true' (e.g. the option is '--foo'), then convert
134 // it to false if there is also an option '--no-foo'. n.b. if the
135 // commandline has '--foo=bar' then $value will not be 'true', and
136 // --no-foo will be ignored.
137 if ($value === true) {
138 // Check if the --no-* option exists. Note that none of the other
139 // alteration apply in the $value == true case, so we can exit early here.
140 $negation_key = 'no-' . $option;
141 return array_key_exists($negation_key, $options) && $options[$negation_key];
144 // If the option is '--foo=0', convert the '0' to 'false' when appropriate.
145 if ($value !== '0') {
149 // The '--foo=0' convertion is only applicable when the default value
150 // is not in the special defaults list. i.e. you get a literal '0'
151 // when your default is a string.
152 return in_array($option, $this->specialDefaults);
155 public function cacheSpecialDefaults($definition)
157 foreach ($definition->getOptions() as $option => $inputOption) {
158 $defaultValue = $inputOption->getDefault();
159 if (($defaultValue === null) || ($defaultValue === true)) {
160 $this->specialDefaults[] = $option;
165 public function getArgsWithoutAppName()
167 $args = $this->arguments();
169 // When called via the Application, the first argument
170 // will be the command name. The Application alters the
171 // input definition to match, adding a 'command' argument
173 if ($this->input->hasArgument('command')) {
177 if ($this->usesOutputInterface) {
178 array_unshift($args, $this->output());
181 if ($this->usesInputInterface) {
182 array_unshift($args, $this->input());
188 public function getArgsAndOptions()
190 // Get passthrough args, and add the options on the end.
191 $args = $this->getArgsWithoutAppName();
192 if ($this->includeOptionsInArgs) {
193 $args['options'] = $this->options();