Completed
Pull Request — master (#119)
by Greg
01:44
created

CommandData::cacheSpecialDefaults()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
rs 9.2
cc 4
eloc 5
nc 3
nop 1
1
<?php
2
namespace Consolidation\AnnotatedCommand;
3
4
use Symfony\Component\Console\Input\ArgvInput;
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Output\OutputInterface;
7
8
class CommandData
9
{
10
    /** var AnnotationData */
11
    protected $annotationData;
12
    /** var InputInterface */
13
    protected $input;
14
    /** var OutputInterface */
15
    protected $output;
16
    /** var boolean */
17
    protected $usesInputInterface;
18
    /** var boolean */
19
    protected $usesOutputInterface;
20
    /** var boolean */
21
    protected $includeOptionsInArgs;
22
    /** var array */
23
    protected $specialDefaults = [];
24
25
    public function __construct(
26
        AnnotationData $annotationData,
27
        InputInterface $input,
28
        OutputInterface $output,
29
        $usesInputInterface = false,
0 ignored issues
show
Unused Code introduced by
The parameter $usesInputInterface is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
30
        $usesOutputInterface = false
0 ignored issues
show
Unused Code introduced by
The parameter $usesOutputInterface is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
31
    ) {
32
        $this->annotationData = $annotationData;
33
        $this->input = $input;
34
        $this->output = $output;
35
        $this->usesInputInterface = false;
36
        $this->usesOutputInterface = false;
37
        $this->includeOptionsInArgs = true;
38
    }
39
40
    /**
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
45
     * @return self
46
     */
47
    public function setUseIOInterfaces($usesInputInterface, $usesOutputInterface)
48
    {
49
        $this->usesInputInterface = $usesInputInterface;
50
        $this->usesOutputInterface = $usesOutputInterface;
51
        return $this;
52
    }
53
54
    /**
55
     * For backwards-compatibility mode only: disable addition of
56
     * options on the end of the arguments list.
57
     */
58
    public function setIncludeOptionsInArgs($includeOptionsInArgs)
59
    {
60
        $this->includeOptionsInArgs = $includeOptionsInArgs;
61
        return $this;
62
    }
63
64
    public function annotationData()
65
    {
66
        return $this->annotationData;
67
    }
68
69
    public function input()
70
    {
71
        return $this->input;
72
    }
73
74
    public function output()
75
    {
76
        return $this->output;
77
    }
78
79
    public function arguments()
80
    {
81
        return $this->input->getArguments();
82
    }
83
84
    public function options()
85
    {
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();
92
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;
99
            }
100
        }
101
102
        return $options;
103
    }
104
105
    /**
106
     * Use 'hasParameterOption()' to attempt to disambiguate option states.
107
     */
108
    protected function getAdjustedOptions()
109
    {
110
        $options = $this->input->getOptions();
111
112
        // If Input isn't an ArgvInput, then return the options as-is.
113
        if (!$this->input instanceof ArgvInput) {
114
            return $options;
115
        }
116
117
        // If we have an ArgvInput, then we can determine if options
118
        // are missing from the command line. Convert any missing
119
        // options with a 'null' value to 'true' or false'.
120
        foreach ($options as $option => $value) {
121
            if ($value === null) {
122
                $options[$option] = $this->input->hasParameterOption("--$option");
123
            }
124
        }
125
126
        return $options;
127
    }
128
129
130
    protected function shouldConvertOptionToFalse($options, $option, $value)
131
    {
132
        // If the value is 'true' (e.g. the option is '--foo'), then convert
133
        // it to false if there is also an option '--no-foo'. n.b. if the
134
        // commandline has '--foo=bar' then $value will not be 'true', and
135
        // --no-foo will be ignored.
136
        if ($value === true) {
137
            // Check if the --no-* option exists. Note that none of the other
138
            // alteration apply in the $value == true case, so we can exit early here.
139
            $negation_key = 'no-' . $option;
140
            return array_key_exists($negation_key, $options) && $options[$negation_key];
141
        }
142
143
        // If the option is '--foo=0', convert the '0' to 'false' when appropriate.
144
        if ($value !== '0') {
145
            return false;
146
        }
147
148
        // The '--foo=0' convertion is only applicable when the default value
149
        // is not in the special defaults list. i.e. you get a literal '0'
150
        // when your default is a string.
151
        return in_array($option, $this->specialDefaults);
152
    }
153
154
    public function cacheSpecialDefaults($definition)
155
    {
156
        foreach ($definition->getOptions() as $option => $inputOption) {
157
            $defaultValue = $inputOption->getDefault();
158
            if (($defaultValue === null) || ($defaultValue === true)) {
159
                $this->specialDefaults[] = $option;
160
            }
161
        }
162
    }
163
164
    public function getArgsWithoutAppName()
165
    {
166
        $args = $this->arguments();
167
168
        // When called via the Application, the first argument
169
        // will be the command name. The Application alters the
170
        // input definition to match, adding a 'command' argument
171
        // to the beginning.
172
        array_shift($args);
173
174
        if ($this->usesOutputInterface) {
175
            array_unshift($args, $this->output());
176
        }
177
178
        if ($this->usesInputInterface) {
179
            array_unshift($args, $this->input());
180
        }
181
182
        return $args;
183
    }
184
185
    public function getArgsAndOptions()
186
    {
187
        // Get passthrough args, and add the options on the end.
188
        $args = $this->getArgsWithoutAppName();
189
        if ($this->includeOptionsInArgs) {
190
            $args['options'] = $this->options();
191
        }
192
        return $args;
193
    }
194
}
195