Parser::arguments()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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