Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / vendor / drush / drush / src / Symfony / LessStrictArgvInput.php
1 <?php
2
3 namespace Drush\Symfony;
4
5 use Symfony\Component\Console\Input\ArgvInput;
6
7 use Symfony\Component\Console\Exception\RuntimeException;
8
9 /**
10  * UnvalidatedArgvInput is an ArgvInput that never reports errors when
11  * extra options are provided.
12  *
13  * If the last argument of the command being called is not an array
14  * argument, then an error will be thrown if there are too many arguments.
15  *
16  * We use this instead of a IndiscriminateInputDefinition in cases where we
17  * know in advance that we wish to disable input validation for all commands.
18  * In contrast, an IndiscriminateInputDefinition is attached to individual
19  * Commands that should accept any option.
20  */
21 class LessStrictArgvInput extends ArgvInput
22 {
23     private $tokens;
24     private $parsed;
25     protected $additionalOptions = [];
26
27     /**
28      * Constructor.
29      *
30      * @param array|null           $argv       An array of parameters from the CLI (in the argv format)
31      * @param InputDefinition|null $definition A InputDefinition instance
32      */
33     public function __construct(array $argv = null, InputDefinition $definition = null)
34     {
35         // We have to duplicate the implementation of ArgvInput
36         // because of liberal use of `private`
37         if (null === $argv) {
38             $argv = $_SERVER['argv'];
39         }
40
41         $this->tokens = $argv;
42         // strip the application name
43         array_shift($this->tokens);
44
45         parent::__construct($argv, $definition);
46     }
47
48     /**
49      * {@inheritdoc}
50      */
51     public function getOption($name)
52     {
53         if (array_key_exists($name, $this->options)) {
54             return $this->options[$name];
55         }
56         if ($this->definition->hasOption($name)) {
57             return $this->definition->getOption($name)->getDefault();
58         }
59         return false;
60     }
61
62     protected function setTokens(array $tokens)
63     {
64         $this->tokens = $tokens;
65     }
66
67     /**
68      * {@inheritdoc}
69      */
70     protected function parse()
71     {
72         $parseOptions = true;
73         $this->parsed = $this->tokens;
74         while (null !== $token = array_shift($this->parsed)) {
75             if ($parseOptions && '' == $token) {
76                 $this->parseArgument($token);
77             } elseif ($parseOptions && '--' == $token) {
78                 $parseOptions = false;
79             } elseif ($parseOptions && 0 === strpos($token, '--')) {
80                 $this->parseLongOption($token);
81             } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
82                 $this->parseShortOption($token);
83             } else {
84                 $this->parseArgument($token);
85             }
86         }
87         // Put back any options that were injected.
88         $this->options += $this->additionalOptions;
89     }
90
91     /**
92      * Parses a short option.
93      *
94      * @param string $token The current token
95      */
96     private function parseShortOption($token)
97     {
98         $name = substr($token, 1);
99
100         if (strlen($name) > 1) {
101             if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
102                 // an option with a value (with no space)
103                 $this->addShortOption($name[0], substr($name, 1));
104             } else {
105                 $this->parseShortOptionSet($name);
106             }
107         } else {
108             $this->addShortOption($name, null);
109         }
110     }
111
112     /**
113      * Parses a short option set.
114      *
115      * @param string $name The current token
116      */
117     private function parseShortOptionSet($name)
118     {
119         $len = strlen($name);
120         for ($i = 0; $i < $len; ++$i) {
121             if (!$this->definition->hasShortcut($name[$i])) {
122                 $this->addShortOption($name[$i]);
123             }
124
125             $option = $this->definition->getOptionForShortcut($name[$i]);
126             if ($option->acceptValue()) {
127                 $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
128
129                 break;
130             } else {
131                 $this->addLongOption($option->getName(), null);
132             }
133         }
134     }
135
136     /**
137      * Parses a long option.
138      *
139      * @param string $token The current token
140      */
141     private function parseLongOption($token)
142     {
143         $name = substr($token, 2);
144
145         if (false !== $pos = strpos($name, '=')) {
146             if (0 === strlen($value = substr($name, $pos + 1))) {
147                 // if no value after "=" then substr() returns "" since php7 only, false before
148                 // see http://php.net/manual/fr/migration70.incompatible.php#119151
149                 if (\PHP_VERSION_ID < 70000 && false === $value) {
150                     $value = '';
151                 }
152                 array_unshift($this->parsed, $value);
153             }
154             $this->addLongOption(substr($name, 0, $pos), $value);
155         } else {
156             $this->addLongOption($name, null);
157         }
158     }
159
160     /**
161      * Parses an argument.
162      *
163      * @param string $token The current token
164      *
165      * @throws RuntimeException When too many arguments are given
166      */
167     private function parseArgument($token)
168     {
169         $c = count($this->arguments);
170
171         // if input is expecting another argument, add it
172         if ($this->definition->hasArgument($c)) {
173             $arg = $this->definition->getArgument($c);
174             $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
175
176         // if last argument isArray(), append token to last argument
177         } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
178             $arg = $this->definition->getArgument($c - 1);
179             $this->arguments[$arg->getName()][] = $token;
180
181         // unexpected argument
182         } else {
183             $all = $this->definition->getArguments();
184             if (count($all)) {
185                 throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s", provided arguments "%s".', implode('" "', array_keys($all)), implode('" "', array_keys($this->arguments))));
186             }
187
188             throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
189         }
190     }
191
192     /**
193      * Adds a short option value.
194      *
195      * @param string $shortcut The short option key
196      * @param mixed  $value    The value for the option
197      *
198      * @throws RuntimeException When option given doesn't exist
199      */
200     private function addShortOption($shortcut, $value)
201     {
202         if (!$this->definition->hasShortcut($shortcut)) {
203             // Hard to know what to do with unknown short options. Maybe
204             // these should be added to the end of the arguments. This would only
205             // be a good strategy if the last argument was an array argument.
206             // We'll try adding as a long option for now.
207             $this->addLongOption($shortcut, $value);
208         }
209
210         $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
211     }
212
213     public function injectAdditionalOptions($additionalOptions)
214     {
215         $this->additionalOptions += $additionalOptions;
216         $this->options += $additionalOptions;
217     }
218
219     /**
220      * Adds a long option value.
221      *
222      * @param string $name  The long option key
223      * @param mixed  $value The value for the option
224      *
225      * @throws RuntimeException When option given doesn't exist
226      */
227     private function addLongOption($name, $value)
228     {
229         if (!$this->definition->hasOption($name)) {
230             // If we don't know anything about this option, then we'll
231             // assume it is generic.
232             $this->options[$name] = $value;
233             return;
234         }
235
236         $option = $this->definition->getOption($name);
237
238         if (null !== $value && !$option->acceptValue()) {
239             throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
240         }
241
242         if (in_array($value, ['', null], true) && $option->acceptValue() && count($this->parsed)) {
243             // if option accepts an optional or mandatory argument
244             // let's see if there is one provided
245             $next = array_shift($this->parsed);
246             if ((isset($next[0]) && '-' !== $next[0]) || in_array($next, ['', null], true)) {
247                 $value = $next;
248             } else {
249                 array_unshift($this->parsed, $next);
250             }
251         }
252
253         if (null === $value) {
254             if ($option->isValueRequired()) {
255                 throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
256             }
257
258             if (!$option->isArray() && !$option->isValueOptional()) {
259                 $value = true;
260             }
261         }
262
263         if ($option->isArray()) {
264             $this->options[$name][] = $value;
265         } else {
266             $this->options[$name] = $value;
267         }
268     }
269
270     /**
271      * {@inheritdoc}
272      */
273     public function getFirstArgument()
274     {
275         foreach ($this->tokens as $token) {
276             if ($token && '-' === $token[0]) {
277                 continue;
278             }
279
280             return $token;
281         }
282     }
283
284     /**
285      * {@inheritdoc}
286      */
287     public function hasParameterOption($values, $onlyParams = false)
288     {
289         $values = (array) $values;
290
291         foreach ($this->tokens as $token) {
292             if ($onlyParams && $token === '--') {
293                 return false;
294             }
295             foreach ($values as $value) {
296                 if ($token === $value || 0 === strpos($token, $value.'=')) {
297                     return true;
298                 }
299             }
300         }
301
302         return false;
303     }
304
305     /**
306      * {@inheritdoc}
307      */
308     public function getParameterOption($values, $default = false, $onlyParams = false)
309     {
310         $values = (array) $values;
311         $tokens = $this->tokens;
312
313         while (0 < count($tokens)) {
314             $token = array_shift($tokens);
315             if ($onlyParams && $token === '--') {
316                 return false;
317             }
318
319             foreach ($values as $value) {
320                 if ($token === $value || 0 === strpos($token, $value.'=')) {
321                     if (false !== $pos = strpos($token, '=')) {
322                         return substr($token, $pos + 1);
323                     }
324
325                     return array_shift($tokens);
326                 }
327             }
328         }
329
330         return $default;
331     }
332
333     /**
334      * Returns a stringified representation of the args passed to the command.
335      *
336      * @return string
337      */
338     public function __toString()
339     {
340         $tokens = array_map(function ($token) {
341             if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
342                 return $match[1].$this->escapeToken($match[2]);
343             }
344
345             if ($token && $token[0] !== '-') {
346                 return $this->escapeToken($token);
347             }
348
349             return $token;
350         }, $this->tokens);
351
352         return implode(' ', $tokens);
353     }
354 }