Completed
Push — version-4-wip ( 86a2b9...5c3ca2 )
by Craig
03:49
created

Parser::isValidArgumentValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace League\CLImate\Argument;
4
5
use League\CLImate\Exceptions\InvalidArgumentException;
6
7
class Parser
8
{
9
    /**
10
     * Filter class to find various types of arguments
11
     *
12
     * @var \League\CLImate\Argument\Filter $filter
13
     */
14
    protected $filter;
15
16
    /**
17
     * Summary builder class
18
     *
19
     * @var \League\CLImate\Argument\Summary $summary
20
     */
21
    protected $summary;
22
23
    protected $trailing;
24
25 868
    public function __construct()
26
    {
27 868
        $this->summary = new Summary();
28 868
    }
29
30
    /**
31
     * @param Filter $filter
32
     * @param Argument[] $arguments
33
     *
34
     * @return \League\CLImate\Argument\Parser
35
     */
36 20
    public function setFilter($filter, $arguments)
37
    {
38 20
        $this->filter = $filter;
39 20
        $this->filter->setArguments($arguments);
40
41 20
        return $this;
42
    }
43
44
    /**
45
     * Parse command line arguments into CLImate arguments.
46
     *
47
     * @param array $argv
48
     *
49
     * @return void
50
     * @throws InvalidArgumentException if required arguments aren't defined.
51
     */
52 20
    public function parse(array $argv = null)
53
    {
54 20
        $cliArguments = $this->arguments($argv);
55
56 20
        if (in_array('--', $cliArguments)) {
57 4
            $cliArguments = $this->removeTrailingArguments($cliArguments);
58
        }
59
60 20
        $unParsedArguments = $this->prefixedArguments($cliArguments);
61
62 20
        $this->nonPrefixedArguments($unParsedArguments);
63
64
        // After parsing find out which arguments were required but not
65
        // defined on the command line.
66 20
        $missingArguments = $this->filter->missing();
67
68 20
        if (count($missingArguments) > 0) {
69 4
            throw new InvalidArgumentException(
70
                'The following arguments are required: '
71 4
                . $this->summary->short($missingArguments) . '.'
72
            );
73
        }
74 16
    }
75
76
    /**
77
     * Get the command name.
78
     *
79
     * @param array $argv
80
     *
81
     * @return string
82
     */
83 4
    public function command(array $argv = null)
84
    {
85 4
        return $this->getCommandAndArguments($argv)['command'];
86
    }
87
88
    /**
89
     * Get the passed arguments.
90
     *
91
     * @param array $argv
92
     *
93
     * @return array
94
     */
95 32
    public function arguments(array $argv = null)
96
    {
97 32
        return $this->getCommandAndArguments($argv)['arguments'];
98
    }
99
100
    /**
101
     * Get the trailing arguments
102
     *
103
     * @return string|null
104
     */
105 4
    public function trailing()
106
    {
107 4
        return $this->trailing;
108
    }
109
110
    /**
111
     * Remove the trailing arguments from the parser and set them aside
112
     *
113
     * @param array $arguments
114
     *
115
     * @return array
116
     */
117 4
    protected function removeTrailingArguments(array $arguments)
118
    {
119 4
        $trailing = array_splice($arguments, array_search('--', $arguments));
120 4
        array_shift($trailing);
121 4
        $this->trailing = implode(' ', $trailing);
122
123 4
        return $arguments;
124
    }
125
126
    /**
127
     * Parse command line options into prefixed CLImate arguments.
128
     *
129
     * Prefixed arguments are arguments with a prefix (-) or a long prefix (--)
130
     * on the command line.
131
     *
132
     * Return the arguments passed on the command line that didn't match up with
133
     * prefixed arguments so they can be assigned to non-prefixed arguments.
134
     *
135
     * @param array $argv
136
     * @return array
137
     */
138 20
    protected function prefixedArguments(array $argv = [])
139
    {
140 20
        foreach ($argv as $key => $passed_argument) {
141 20
            $argv = $this->trySettingArgumentValue($argv, $key, $passed_argument);
142
        }
143
144
        // Send un-parsed arguments back upstream.
145 20
        return array_values($argv);
146
    }
147
148
    /**
149
     * Parse unset command line options into non-prefixed CLImate arguments.
150
     *
151
     * Non-prefixed arguments are parsed after the prefixed arguments on the
152
     * command line, in the order that they're defined in the script.
153
     *
154
     * @param array $unParsedArguments
155
     */
156 20
    protected function nonPrefixedArguments(array $unParsedArguments = [])
157
    {
158 20
        foreach ($this->filter->withoutPrefix() as $key => $argument) {
159 4
            if (isset($unParsedArguments[$key])) {
160 4
                $argument->setValue($unParsedArguments[$key]);
161
            }
162
        }
163 20
    }
164
165
    /**
166
     * Parse the name and value of the argument passed in
167
     *
168
     * @param string $cliArgument
169
     * @return string[] [$name, $value]
170
     */
171 20
    protected function getNameAndValue($cliArgument)
172
    {
173
        // Look for arguments defined in the "key=value" format.
174 20
        if (strpos($cliArgument, '=') !== false) {
175 8
            return explode('=', $cliArgument, 2);
176
        }
177
178
        // If the argument isn't in "key=value" format then assume it's in
179
        // "key value" format and define the value after we've found the
180
        // matching CLImate argument.
181 20
        return [$cliArgument, null];
182
    }
183
184
    /**
185
     * Attempt to set the an argument's value and remove applicable
186
     * arguments from array
187
     *
188
     * @param array $argv
189
     * @param int $key
190
     * @param string $passed_argument
191
     *
192
     * @return array The new $argv
193
     */
194 20
    protected function trySettingArgumentValue($argv, $key, $passed_argument)
195
    {
196 20
        list($name, $value) = $this->getNameAndValue($passed_argument);
197
198
        // Look for the argument in our defined $arguments
199
        // and assign their value.
200 20
        if (!($argument = $this->findPrefixedArgument($name))) {
201 16
            return $argv;
202
        }
203
204
        // We found an argument key, so take it out of the array.
205 20
        unset($argv[$key]);
206
207 20
        return $this->setArgumentValue($argv, $argument, $key, $value);
208
    }
209
210
    /**
211
     * Set the argument's value
212
     *
213
     * @param array $argv
214
     * @param Argument $argument
215
     * @param int $key
216
     * @param string|null $value
217
     *
218
     * @return array The new $argv
219
     */
220 20
    protected function setArgumentValue($argv, $argument, $key, $value)
221
    {
222
        // Arguments are given the value true if they only need to
223
        // be defined on the command line to be set.
224 20
        if ($argument->noValue()) {
225 4
            $argument->setValue(true);
226 4
            return $argv;
227
        }
228
229 20
        if (is_null($value)) {
230 20
            if (count($argv) === 0) {
231 4
                return $argv;
232
            }
233
234
            // If the value wasn't previously defined in "key=value"
235
            // format then define it from the next command argument.
236 16
            $nextArgvValue = $argv[$key + 1];
237 16
            if ($this->isValidArgumentValue($nextArgvValue)) {
238 16
                $argument->setValue($nextArgvValue);
239 16
                unset($argv[$key + 1]);
240 16
                return $argv;
241
            }
242
        }
243
244 12
        $argument->setValue($value);
245
246 12
        return $argv;
247
    }
248
249
    /**
250
     * Check if the value is considered a valid input value.
251
     *
252
     * @param $argumentValue
253
     * @return bool
254
     */
255 16
    protected function isValidArgumentValue($argumentValue)
256
    {
257 16
        return empty($this->findPrefixedArgument($argumentValue));
258
    }
259
260
    /**
261
     * Search for argument in defined prefix arguments
262
     *
263
     * @param string $name
264
     *
265
     * @return Argument|false
266
     */
267 20
    protected function findPrefixedArgument($name)
268
    {
269 20
        foreach ($this->filter->withPrefix() as $argument) {
270 20
            if (in_array($name, ["-{$argument->prefix()}", "--{$argument->longPrefix()}"])) {
271 20
                return $argument;
272
            }
273
        }
274
275 16
        return false;
276
    }
277
278
    /**
279
     * Pull a command name and arguments from $argv.
280
     *
281
     * @param array $argv
282
     * @return array
283
     */
284 36
    protected function getCommandAndArguments(array $argv = null)
285
    {
286
        // If no $argv is provided then use the global PHP defined $argv.
287 36
        if (is_null($argv)) {
288
            global $argv;
289
        }
290
291 36
        $arguments = $argv;
292 36
        $command   = array_shift($arguments);
293
294 36
        return compact('arguments', 'command');
295
    }
296
}
297