Completed
Pull Request — master (#83)
by Sergii
04:59
created

Parser::removeTrailingArguments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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