Completed
Push — master ( 1e240b...42513d )
by Joe
02:04
created

Parser::trailing()   A

Complexity

Conditions 1
Paths 1

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 4
Code Lines 2

Code Coverage

Tests 3
CRAP Score 1

Importance

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