Completed
Push — master ( 84dee3...084275 )
by Michal
36:03
created

ArgvInput::getParameterOption()   C

Complexity

Conditions 8
Paths 6

Size

Total Lines 24
Code Lines 13

Duplication

Lines 3
Ratio 12.5 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 3
loc 24
rs 5.7377
cc 8
eloc 13
nc 6
nop 3
1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Component\Console\Input;
13
14
use Symfony\Component\Console\Exception\RuntimeException;
15
16
/**
17
 * ArgvInput represents an input coming from the CLI arguments.
18
 *
19
 * Usage:
20
 *
21
 *     $input = new ArgvInput();
22
 *
23
 * By default, the `$_SERVER['argv']` array is used for the input values.
24
 *
25
 * This can be overridden by explicitly passing the input values in the constructor:
26
 *
27
 *     $input = new ArgvInput($_SERVER['argv']);
28
 *
29
 * If you pass it yourself, don't forget that the first element of the array
30
 * is the name of the running application.
31
 *
32
 * When passing an argument to the constructor, be sure that it respects
33
 * the same rules as the argv one. It's almost always better to use the
34
 * `StringInput` when you want to provide your own input.
35
 *
36
 * @author Fabien Potencier <[email protected]>
37
 *
38
 * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
39
 * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
40
 */
41
class ArgvInput extends Input
42
{
43
    private $tokens;
44
    private $parsed;
45
46
    /**
47
     * Constructor.
48
     *
49
     * @param array           $argv       An array of parameters from the CLI (in the argv format)
50
     * @param InputDefinition $definition A InputDefinition instance
51
     */
52
    public function __construct(array $argv = null, InputDefinition $definition = null)
53
    {
54
        if (null === $argv) {
55
            $argv = $_SERVER['argv'];
56
        }
57
58
        // strip the application name
59
        array_shift($argv);
60
61
        $this->tokens = $argv;
62
63
        parent::__construct($definition);
64
    }
65
66
    protected function setTokens(array $tokens)
67
    {
68
        $this->tokens = $tokens;
69
    }
70
71
    /**
72
     * Processes command line arguments.
73
     */
74
    protected function parse()
75
    {
76
        $parseOptions = true;
77
        $this->parsed = $this->tokens;
78
        while (null !== $token = array_shift($this->parsed)) {
79
            if ($parseOptions && '' == $token) {
80
                $this->parseArgument($token);
81
            } elseif ($parseOptions && '--' == $token) {
82
                $parseOptions = false;
83
            } elseif ($parseOptions && 0 === strpos($token, '--')) {
84
                $this->parseLongOption($token);
85
            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
86
                $this->parseShortOption($token);
87
            } else {
88
                $this->parseArgument($token);
89
            }
90
        }
91
    }
92
93
    /**
94
     * Parses a short option.
95
     *
96
     * @param string $token The current token.
97
     */
98
    private function parseShortOption($token)
99
    {
100
        $name = substr($token, 1);
101
102
        if (strlen($name) > 1) {
103
            if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
104
                // an option with a value (with no space)
105
                $this->addShortOption($name[0], substr($name, 1));
106
            } else {
107
                $this->parseShortOptionSet($name);
108
            }
109
        } else {
110
            $this->addShortOption($name, null);
111
        }
112
    }
113
114
    /**
115
     * Parses a short option set.
116
     *
117
     * @param string $name The current token
118
     *
119
     * @throws RuntimeException When option given doesn't exist
120
     */
121
    private function parseShortOptionSet($name)
122
    {
123
        $len = strlen($name);
124
        for ($i = 0; $i < $len; ++$i) {
125
            if (!$this->definition->hasShortcut($name[$i])) {
126
                throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
127
            }
128
129
            $option = $this->definition->getOptionForShortcut($name[$i]);
130
            if ($option->acceptValue()) {
131
                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
132
133
                break;
134
            } else {
135
                $this->addLongOption($option->getName(), null);
136
            }
137
        }
138
    }
139
140
    /**
141
     * Parses a long option.
142
     *
143
     * @param string $token The current token
144
     */
145 View Code Duplication
    private function parseLongOption($token)
146
    {
147
        $name = substr($token, 2);
148
149
        if (false !== $pos = strpos($name, '=')) {
150
            $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
151
        } else {
152
            $this->addLongOption($name, null);
153
        }
154
    }
155
156
    /**
157
     * Parses an argument.
158
     *
159
     * @param string $token The current token
160
     *
161
     * @throws RuntimeException When too many arguments are given
162
     */
163
    private function parseArgument($token)
164
    {
165
        $c = count($this->arguments);
166
167
        // if input is expecting another argument, add it
168
        if ($this->definition->hasArgument($c)) {
169
            $arg = $this->definition->getArgument($c);
170
            $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;
171
172
        // if last argument isArray(), append token to last argument
173
        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
174
            $arg = $this->definition->getArgument($c - 1);
175
            $this->arguments[$arg->getName()][] = $token;
176
177
        // unexpected argument
178
        } else {
179
            throw new RuntimeException('Too many arguments.');
180
        }
181
    }
182
183
    /**
184
     * Adds a short option value.
185
     *
186
     * @param string $shortcut The short option key
187
     * @param mixed  $value    The value for the option
188
     *
189
     * @throws RuntimeException When option given doesn't exist
190
     */
191 View Code Duplication
    private function addShortOption($shortcut, $value)
192
    {
193
        if (!$this->definition->hasShortcut($shortcut)) {
194
            throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
195
        }
196
197
        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
198
    }
199
200
    /**
201
     * Adds a long option value.
202
     *
203
     * @param string $name  The long option key
204
     * @param mixed  $value The value for the option
205
     *
206
     * @throws RuntimeException When option given doesn't exist
207
     */
208
    private function addLongOption($name, $value)
209
    {
210
        if (!$this->definition->hasOption($name)) {
211
            throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
212
        }
213
214
        $option = $this->definition->getOption($name);
215
216
        // Convert empty values to null
217
        if (!isset($value[0])) {
218
            $value = null;
219
        }
220
221
        if (null !== $value && !$option->acceptValue()) {
222
            throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
223
        }
224
225
        if (null === $value && $option->acceptValue() && count($this->parsed)) {
226
            // if option accepts an optional or mandatory argument
227
            // let's see if there is one provided
228
            $next = array_shift($this->parsed);
229
            if (isset($next[0]) && '-' !== $next[0]) {
230
                $value = $next;
231
            } elseif (empty($next)) {
232
                $value = '';
233
            } else {
234
                array_unshift($this->parsed, $next);
235
            }
236
        }
237
238
        if (null === $value) {
239
            if ($option->isValueRequired()) {
240
                throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
241
            }
242
243
            if (!$option->isArray()) {
244
                $value = $option->isValueOptional() ? $option->getDefault() : true;
245
            }
246
        }
247
248
        if ($option->isArray()) {
249
            $this->options[$name][] = $value;
250
        } else {
251
            $this->options[$name] = $value;
252
        }
253
    }
254
255
    /**
256
     * Returns the first argument from the raw parameters (not parsed).
257
     *
258
     * @return string The value of the first argument or null otherwise
259
     */
260
    public function getFirstArgument()
261
    {
262
        foreach ($this->tokens as $token) {
263
            if ($token && '-' === $token[0]) {
264
                continue;
265
            }
266
267
            return $token;
268
        }
269
    }
270
271
    /**
272
     * Returns true if the raw parameters (not parsed) contain a value.
273
     *
274
     * This method is to be used to introspect the input parameters
275
     * before they have been validated. It must be used carefully.
276
     *
277
     * @param string|array $values     The value(s) to look for in the raw parameters (can be an array)
278
     * @param bool         $onlyParams Only check real parameters, skip those following an end of options (--) signal
279
     *
280
     * @return bool true if the value is contained in the raw parameters
281
     */
282
    public function hasParameterOption($values, $onlyParams = false)
283
    {
284
        $values = (array) $values;
285
286
        foreach ($this->tokens as $token) {
287
            if ($onlyParams && $token === '--') {
288
                return false;
289
            }
290
            foreach ($values as $value) {
291
                if ($token === $value || 0 === strpos($token, $value.'=')) {
292
                    return true;
293
                }
294
            }
295
        }
296
297
        return false;
298
    }
299
300
    /**
301
     * Returns the value of a raw option (not parsed).
302
     *
303
     * This method is to be used to introspect the input parameters
304
     * before they have been validated. It must be used carefully.
305
     *
306
     * @param string|array $values     The value(s) to look for in the raw parameters (can be an array)
307
     * @param mixed        $default    The default value to return if no result is found
308
     * @param bool         $onlyParams Only check real parameters, skip those following an end of options (--) signal
309
     *
310
     * @return mixed The option value
311
     */
312
    public function getParameterOption($values, $default = false, $onlyParams = false)
313
    {
314
        $values = (array) $values;
315
        $tokens = $this->tokens;
316
317
        while (0 < count($tokens)) {
318
            $token = array_shift($tokens);
319
            if ($onlyParams && $token === '--') {
320
                return false;
321
            }
322
323
            foreach ($values as $value) {
324
                if ($token === $value || 0 === strpos($token, $value.'=')) {
325 View Code Duplication
                    if (false !== $pos = strpos($token, '=')) {
326
                        return substr($token, $pos + 1);
327
                    }
328
329
                    return array_shift($tokens);
330
                }
331
            }
332
        }
333
334
        return $default;
335
    }
336
337
    /**
338
     * Returns a stringified representation of the args passed to the command.
339
     *
340
     * @return string
341
     */
342
    public function __toString()
343
    {
344
        $tokens = array_map(function ($token) {
345
            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
346
                return $match[1].$this->escapeToken($match[2]);
347
            }
348
349
            if ($token && $token[0] !== '-') {
350
                return $this->escapeToken($token);
351
            }
352
353
            return $token;
354
        }, $this->tokens);
355
356
        return implode(' ', $tokens);
357
    }
358
}
359