Completed
Push — master ( fc0cc4...b103fc )
by Joe
06:18
created

Parser   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 97.44%

Importance

Changes 7
Bugs 3 Features 0
Metric Value
wmc 27
c 7
b 3
f 0
lcom 1
cbo 3
dl 0
loc 274
ccs 76
cts 78
cp 0.9744
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A trySettingArgumentValue() 0 15 2
A __construct() 0 4 1
A setFilter() 0 7 1
A parse() 0 23 3
A command() 0 4 1
A arguments() 0 4 1
A trailing() 0 4 1
A removeTrailingArguments() 0 8 1
A prefixedArguments() 0 9 2
A nonPrefixedArguments() 0 8 3
A getNameAndValue() 0 12 2
B setArgumentValue() 0 25 4
A findPrefixedArgument() 0 10 3
A getCommandAndArguments() 0 12 2
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 932
    public function __construct()
24
    {
25 932
        $this->summary = new Summary();
26 932
    }
27
28
    /**
29
     * @param Filter $filter
30
     * @param Argument[] $arguments
31
     *
32
     * @return \League\CLImate\Argument\Parser
33
     */
34 16
    public function setFilter($filter, $arguments)
35
    {
36 16
        $this->filter = $filter;
37 16
        $this->filter->setArguments($arguments);
38
39 16
        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 16
    public function parse(array $argv = null)
49
    {
50 16
        $cliArguments = $this->arguments($argv);
51
52 16
        if (in_array('--', $cliArguments)) {
53 4
            $cliArguments = $this->removeTrailingArguments($cliArguments);
54 4
        }
55
56 16
        $unParsedArguments = $this->prefixedArguments($cliArguments);
57
58 16
        $this->nonPrefixedArguments($unParsedArguments);
59
60
        // After parsing find out which arguments were required but not
61
        // defined on the command line.
62 16
        $missingArguments = $this->filter->missing();
63
64 16
        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 12
    }
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 20
    public function arguments(array $argv = null)
92
    {
93 20
        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 16
    protected function prefixedArguments(array $argv = [])
135
    {
136 16
        foreach ($argv as $key => $passed_argument) {
137 16
            $argv = $this->trySettingArgumentValue($argv, $key, $passed_argument);
138 16
        }
139
140
        // Send un-parsed arguments back upstream.
141 16
        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 16
    protected function nonPrefixedArguments(array $unParsedArguments = [])
153
    {
154 16
        foreach ($this->filter->withoutPrefix() as $key => $argument) {
155 4
            if (isset($unParsedArguments[$key])) {
156 4
                $argument->setValue($unParsedArguments[$key]);
157 4
            }
158 16
        }
159 16
    }
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 16
    protected function getNameAndValue($cliArgument)
168
    {
169
        // Look for arguments defined in the "key=value" format.
170 16
        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 16
        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 16
    protected function trySettingArgumentValue($argv, $key, $passed_argument)
191
    {
192 16
        list($name, $value) = $this->getNameAndValue($passed_argument);
193
194
        // Look for the argument in our defined $arguments
195
        // and assign their value.
196 16
        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 16
        unset($argv[$key]);
202
203 16
        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 16
    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 16
        if ($argument->noValue()) {
221 4
            $argument->setValue(true);
222 4
            return $argv;
223
        }
224
225 16
        if (is_null($value)) {
226 16
            if (count($argv) === 0) {
227 4
                return $argv;
228
            }
229
230
            // If the value wasn't previously defined in "key=value"
231
            // format then define it from the next command argument.
232 12
            $argument->setValue($argv[++$key]);
233 12
            unset($argv[$key]);
234 12
            return $argv;
235
        }
236
237 8
        $argument->setValue($value);
238
239 8
        return $argv;
240
    }
241
242
    /**
243
     * Search for argument in defined prefix arguments
244
     *
245
     * @param string $name
246
     *
247
     * @return Argument|false
248
     */
249 16
    protected function findPrefixedArgument($name)
250
    {
251 16
        foreach ($this->filter->withPrefix() as $argument) {
252 16
            if (in_array($name, ["-{$argument->prefix()}", "--{$argument->longPrefix()}"])) {
253 16
                return $argument;
254
            }
255 12
        }
256
257 12
        return false;
258
    }
259
260
    /**
261
     * Pull a command name and arguments from $argv.
262
     *
263
     * @param array $argv
264
     * @return array
265
     */
266 24
    protected function getCommandAndArguments(array $argv = null)
267
    {
268
        // If no $argv is provided then use the global PHP defined $argv.
269 24
        if (is_null($argv)) {
270
            global $argv;
271
        }
272
273 24
        $arguments = $argv;
274 24
        $command   = array_shift($arguments);
275
276 24
        return compact('arguments', 'command');
277
    }
278
}
279