Completed
Push — master ( 6d71ae...f255f3 )
by Greg
01:49
created

CommandData::setFormatterOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
namespace Consolidation\AnnotatedCommand;
3
4
use Consolidation\OutputFormatters\Options\FormatterOptions;
5
use Symfony\Component\Console\Input\ArgvInput;
6
use Symfony\Component\Console\Input\InputInterface;
7
use Symfony\Component\Console\Output\OutputInterface;
8
9
class CommandData
10
{
11
    /** var AnnotationData */
12
    protected $annotationData;
13
    /** var InputInterface */
14
    protected $input;
15
    /** var OutputInterface */
16
    protected $output;
17
    /** var boolean */
18
    protected $includeOptionsInArgs;
19
    /** var array */
20
    protected $specialDefaults = [];
21
    /** @var string[] */
22
    protected $injectedInstances = [];
23
    /** @var FormatterOptions */
24
    protected $formatterOptions;
25
26
    public function __construct(
27
        AnnotationData $annotationData,
28
        InputInterface $input,
29
        OutputInterface $output
30
    ) {
31
        $this->annotationData = $annotationData;
32
        $this->input = $input;
33
        $this->output = $output;
34
        $this->includeOptionsInArgs = true;
35
    }
36
37
    /**
38
     * For internal use only; inject an instance to be passed back
39
     * to the command callback as a parameter.
40
     */
41
    public function injectInstance($injectedInstance)
42
    {
43
        array_unshift($this->injectedInstances, $injectedInstance);
44
        return $this;
45
    }
46
47
    /**
48
     * Provide a reference to the instances that will be added to the
49
     * beginning of the parameter list when the command callback is invoked.
50
     */
51
    public function injectedInstances()
52
    {
53
        return $this->injectedInstances;
54
    }
55
56
    /**
57
     * For backwards-compatibility mode only: disable addition of
58
     * options on the end of the arguments list.
59
     */
60
    public function setIncludeOptionsInArgs($includeOptionsInArgs)
61
    {
62
        $this->includeOptionsInArgs = $includeOptionsInArgs;
63
        return $this;
64
    }
65
66
    public function annotationData()
67
    {
68
        return $this->annotationData;
69
    }
70
71
    public function formatterOptions()
72
    {
73
        return $this->formatterOptions;
74
    }
75
76
    public function setFormatterOptions($formatterOptions)
77
    {
78
        $this->formatterOptions = $formatterOptions;
79
    }
80
81
    public function input()
82
    {
83
        return $this->input;
84
    }
85
86
    public function output()
87
    {
88
        return $this->output;
89
    }
90
91
    public function arguments()
92
    {
93
        return $this->input->getArguments();
94
    }
95
96
    public function options()
97
    {
98
        // We cannot tell the difference between '--foo' (an option without
99
        // a value) and the absence of '--foo' when the option has an optional
100
        // value, and the current vallue of the option is 'null' using only
101
        // the public methods of InputInterface. We'll try to figure out
102
        // which is which by other means here.
103
        $options = $this->getAdjustedOptions();
104
105
        // Make two conversions here:
106
        // --foo=0 wil convert $value from '0' to 'false' for binary options.
107
        // --foo with $value of 'true' will be forced to 'false' if --no-foo exists.
108
        foreach ($options as $option => $value) {
109
            if ($this->shouldConvertOptionToFalse($options, $option, $value)) {
110
                $options[$option] = false;
111
            }
112
        }
113
114
        return $options;
115
    }
116
117
    /**
118
     * Use 'hasParameterOption()' to attempt to disambiguate option states.
119
     */
120
    protected function getAdjustedOptions()
121
    {
122
        $options = $this->input->getOptions();
123
124
        // If Input isn't an ArgvInput, then return the options as-is.
125
        if (!$this->input instanceof ArgvInput) {
126
            return $options;
127
        }
128
129
        // If we have an ArgvInput, then we can determine if options
130
        // are missing from the command line. If the option value is
131
        // missing from $input, then we will keep the value `null`.
132
        // If it is present, but has no explicit value, then change it its
133
        // value to `true`.
134
        foreach ($options as $option => $value) {
135
            if (($value === null) && ($this->input->hasParameterOption("--$option"))) {
136
                $options[$option] = true;
137
            }
138
        }
139
140
        return $options;
141
    }
142
143
    protected function shouldConvertOptionToFalse($options, $option, $value)
144
    {
145
        // If the value is 'true' (e.g. the option is '--foo'), then convert
146
        // it to false if there is also an option '--no-foo'. n.b. if the
147
        // commandline has '--foo=bar' then $value will not be 'true', and
148
        // --no-foo will be ignored.
149
        if ($value === true) {
150
            // Check if the --no-* option exists. Note that none of the other
151
            // alteration apply in the $value == true case, so we can exit early here.
152
            $negation_key = 'no-' . $option;
153
            return array_key_exists($negation_key, $options) && $options[$negation_key];
154
        }
155
156
        // If the option is '--foo=0', convert the '0' to 'false' when appropriate.
157
        if ($value !== '0') {
158
            return false;
159
        }
160
161
        // The '--foo=0' convertion is only applicable when the default value
162
        // is not in the special defaults list. i.e. you get a literal '0'
163
        // when your default is a string.
164
        return in_array($option, $this->specialDefaults);
165
    }
166
167
    public function cacheSpecialDefaults($definition)
168
    {
169
        foreach ($definition->getOptions() as $option => $inputOption) {
170
            $defaultValue = $inputOption->getDefault();
171
            if (($defaultValue === null) || ($defaultValue === true)) {
172
                $this->specialDefaults[] = $option;
173
            }
174
        }
175
    }
176
177
    public function getArgsWithoutAppName()
178
    {
179
        $args = $this->arguments();
180
181
        // When called via the Application, the first argument
182
        // will be the command name. The Application alters the
183
        // input definition to match, adding a 'command' argument
184
        // to the beginning.
185
        if ($this->input->hasArgument('command')) {
186
            array_shift($args);
187
        }
188
189
        return $args;
190
    }
191
192
    public function getArgsAndOptions()
193
    {
194
        // Get passthrough args, and add the options on the end.
195
        $args = $this->getArgsWithoutAppName();
196
        if ($this->includeOptionsInArgs) {
197
            $args['options'] = $this->options();
198
        }
199
        return $args;
200
    }
201
}
202