annotate vendor/symfony/console/Input/ArgvInput.php @ 19:fa3358dc1485 tip

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