Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
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 <fabien@symfony.com>
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 array $tokens;
44
    private array $parsed;
45
 
46
    public function __construct(array $argv = null, InputDefinition $definition = null)
47
    {
48
        $argv ??= $_SERVER['argv'] ?? [];
49
 
50
        // strip the application name
51
        array_shift($argv);
52
 
53
        $this->tokens = $argv;
54
 
55
        parent::__construct($definition);
56
    }
57
 
58
    protected function setTokens(array $tokens)
59
    {
60
        $this->tokens = $tokens;
61
    }
62
 
63
    protected function parse()
64
    {
65
        $parseOptions = true;
66
        $this->parsed = $this->tokens;
67
        while (null !== $token = array_shift($this->parsed)) {
68
            $parseOptions = $this->parseToken($token, $parseOptions);
69
        }
70
    }
71
 
72
    protected function parseToken(string $token, bool $parseOptions): bool
73
    {
74
        if ($parseOptions && '' == $token) {
75
            $this->parseArgument($token);
76
        } elseif ($parseOptions && '--' == $token) {
77
            return false;
78
        } elseif ($parseOptions && str_starts_with($token, '--')) {
79
            $this->parseLongOption($token);
80
        } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
81
            $this->parseShortOption($token);
82
        } else {
83
            $this->parseArgument($token);
84
        }
85
 
86
        return $parseOptions;
87
    }
88
 
89
    /**
90
     * Parses a short option.
91
     */
92
    private function parseShortOption(string $token)
93
    {
94
        $name = substr($token, 1);
95
 
96
        if (\strlen($name) > 1) {
97
            if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
98
                // an option with a value (with no space)
99
                $this->addShortOption($name[0], substr($name, 1));
100
            } else {
101
                $this->parseShortOptionSet($name);
102
            }
103
        } else {
104
            $this->addShortOption($name, null);
105
        }
106
    }
107
 
108
    /**
109
     * Parses a short option set.
110
     *
111
     * @throws RuntimeException When option given doesn't exist
112
     */
113
    private function parseShortOptionSet(string $name)
114
    {
115
        $len = \strlen($name);
116
        for ($i = 0; $i < $len; ++$i) {
117
            if (!$this->definition->hasShortcut($name[$i])) {
118
                $encoding = mb_detect_encoding($name, null, true);
119
                throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
120
            }
121
 
122
            $option = $this->definition->getOptionForShortcut($name[$i]);
123
            if ($option->acceptValue()) {
124
                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
125
 
126
                break;
127
            } else {
128
                $this->addLongOption($option->getName(), null);
129
            }
130
        }
131
    }
132
 
133
    /**
134
     * Parses a long option.
135
     */
136
    private function parseLongOption(string $token)
137
    {
138
        $name = substr($token, 2);
139
 
140
        if (false !== $pos = strpos($name, '=')) {
141
            if ('' === $value = substr($name, $pos + 1)) {
142
                array_unshift($this->parsed, $value);
143
            }
144
            $this->addLongOption(substr($name, 0, $pos), $value);
145
        } else {
146
            $this->addLongOption($name, null);
147
        }
148
    }
149
 
150
    /**
151
     * Parses an argument.
152
     *
153
     * @throws RuntimeException When too many arguments are given
154
     */
155
    private function parseArgument(string $token)
156
    {
157
        $c = \count($this->arguments);
158
 
159
        // if input is expecting another argument, add it
160
        if ($this->definition->hasArgument($c)) {
161
            $arg = $this->definition->getArgument($c);
162
            $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
163
 
164
        // if last argument isArray(), append token to last argument
165
        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
166
            $arg = $this->definition->getArgument($c - 1);
167
            $this->arguments[$arg->getName()][] = $token;
168
 
169
        // unexpected argument
170
        } else {
171
            $all = $this->definition->getArguments();
172
            $symfonyCommandName = null;
173
            if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) {
174
                $symfonyCommandName = $this->arguments['command'] ?? null;
175
                unset($all[$key]);
176
            }
177
 
178
            if (\count($all)) {
179
                if ($symfonyCommandName) {
180
                    $message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all)));
181
                } else {
182
                    $message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)));
183
                }
184
            } elseif ($symfonyCommandName) {
185
                $message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token);
186
            } else {
187
                $message = sprintf('No arguments expected, got "%s".', $token);
188
            }
189
 
190
            throw new RuntimeException($message);
191
        }
192
    }
193
 
194
    /**
195
     * Adds a short option value.
196
     *
197
     * @throws RuntimeException When option given doesn't exist
198
     */
199
    private function addShortOption(string $shortcut, mixed $value)
200
    {
201
        if (!$this->definition->hasShortcut($shortcut)) {
202
            throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
203
        }
204
 
205
        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
206
    }
207
 
208
    /**
209
     * Adds a long option value.
210
     *
211
     * @throws RuntimeException When option given doesn't exist
212
     */
213
    private function addLongOption(string $name, mixed $value)
214
    {
215
        if (!$this->definition->hasOption($name)) {
216
            if (!$this->definition->hasNegation($name)) {
217
                throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
218
            }
219
 
220
            $optionName = $this->definition->negationToName($name);
221
            if (null !== $value) {
222
                throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
223
            }
224
            $this->options[$optionName] = false;
225
 
226
            return;
227
        }
228
 
229
        $option = $this->definition->getOption($name);
230
 
231
        if (null !== $value && !$option->acceptValue()) {
232
            throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
233
        }
234
 
235
        if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {
236
            // if option accepts an optional or mandatory argument
237
            // let's see if there is one provided
238
            $next = array_shift($this->parsed);
239
            if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
240
                $value = $next;
241
            } else {
242
                array_unshift($this->parsed, $next);
243
            }
244
        }
245
 
246
        if (null === $value) {
247
            if ($option->isValueRequired()) {
248
                throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
249
            }
250
 
251
            if (!$option->isArray() && !$option->isValueOptional()) {
252
                $value = true;
253
            }
254
        }
255
 
256
        if ($option->isArray()) {
257
            $this->options[$name][] = $value;
258
        } else {
259
            $this->options[$name] = $value;
260
        }
261
    }
262
 
263
    public function getFirstArgument(): ?string
264
    {
265
        $isOption = false;
266
        foreach ($this->tokens as $i => $token) {
267
            if ($token && '-' === $token[0]) {
268
                if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) {
269
                    continue;
270
                }
271
 
272
                // If it's a long option, consider that everything after "--" is the option name.
273
                // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
274
                $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
275
                if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
276
                    // noop
277
                } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
278
                    $isOption = true;
279
                }
280
 
281
                continue;
282
            }
283
 
284
            if ($isOption) {
285
                $isOption = false;
286
                continue;
287
            }
288
 
289
            return $token;
290
        }
291
 
292
        return null;
293
    }
294
 
295
    public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
296
    {
297
        $values = (array) $values;
298
 
299
        foreach ($this->tokens as $token) {
300
            if ($onlyParams && '--' === $token) {
301
                return false;
302
            }
303
            foreach ($values as $value) {
304
                // Options with values:
305
                //   For long options, test for '--option=' at beginning
306
                //   For short options, test for '-o' at beginning
307
                $leading = str_starts_with($value, '--') ? $value.'=' : $value;
308
                if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) {
309
                    return true;
310
                }
311
            }
312
        }
313
 
314
        return false;
315
    }
316
 
317
    public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
318
    {
319
        $values = (array) $values;
320
        $tokens = $this->tokens;
321
 
322
        while (0 < \count($tokens)) {
323
            $token = array_shift($tokens);
324
            if ($onlyParams && '--' === $token) {
325
                return $default;
326
            }
327
 
328
            foreach ($values as $value) {
329
                if ($token === $value) {
330
                    return array_shift($tokens);
331
                }
332
                // Options with values:
333
                //   For long options, test for '--option=' at beginning
334
                //   For short options, test for '-o' at beginning
335
                $leading = str_starts_with($value, '--') ? $value.'=' : $value;
336
                if ('' !== $leading && str_starts_with($token, $leading)) {
337
                    return substr($token, \strlen($leading));
338
                }
339
            }
340
        }
341
 
342
        return $default;
343
    }
344
 
345
    /**
346
     * Returns a stringified representation of the args passed to the command.
347
     */
348
    public function __toString(): string
349
    {
350
        $tokens = array_map(function ($token) {
351
            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
352
                return $match[1].$this->escapeToken($match[2]);
353
            }
354
 
355
            if ($token && '-' !== $token[0]) {
356
                return $this->escapeToken($token);
357
            }
358
 
359
            return $token;
360
        }, $this->tokens);
361
 
362
        return implode(' ', $tokens);
363
    }
364
}