4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Console\Input;
14 use Symfony\Component\Console\Exception\RuntimeException;
17 * ArgvInput represents an input coming from the CLI arguments.
21 * $input = new ArgvInput();
23 * By default, the `$_SERVER['argv']` array is used for the input values.
25 * This can be overridden by explicitly passing the input values in the constructor:
27 * $input = new ArgvInput($_SERVER['argv']);
29 * If you pass it yourself, don't forget that the first element of the array
30 * is the name of the running application.
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.
36 * @author Fabien Potencier <fabien@symfony.com>
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
41 class ArgvInput extends Input
47 * @param array|null $argv An array of parameters from the CLI (in the argv format)
48 * @param InputDefinition|null $definition A InputDefinition instance
50 public function __construct(array $argv = null, InputDefinition $definition = null)
53 $argv = $_SERVER['argv'];
56 // strip the application name
59 $this->tokens = $argv;
61 parent::__construct($definition);
64 protected function setTokens(array $tokens)
66 $this->tokens = $tokens;
72 protected function parse()
75 $this->parsed = $this->tokens;
76 while (null !== $token = array_shift($this->parsed)) {
77 if ($parseOptions && '' == $token) {
78 $this->parseArgument($token);
79 } elseif ($parseOptions && '--' == $token) {
80 $parseOptions = false;
81 } elseif ($parseOptions && 0 === strpos($token, '--')) {
82 $this->parseLongOption($token);
83 } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
84 $this->parseShortOption($token);
86 $this->parseArgument($token);
92 * Parses a short option.
94 * @param string $token The current token
96 private function parseShortOption($token)
98 $name = substr($token, 1);
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));
105 $this->parseShortOptionSet($name);
108 $this->addShortOption($name, null);
113 * Parses a short option set.
115 * @param string $name The current token
117 * @throws RuntimeException When option given doesn't exist
119 private function parseShortOptionSet($name)
121 $len = \strlen($name);
122 for ($i = 0; $i < $len; ++$i) {
123 if (!$this->definition->hasShortcut($name[$i])) {
124 $encoding = mb_detect_encoding($name, null, true);
125 throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
128 $option = $this->definition->getOptionForShortcut($name[$i]);
129 if ($option->acceptValue()) {
130 $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
134 $this->addLongOption($option->getName(), null);
140 * Parses a long option.
142 * @param string $token The current token
144 private function parseLongOption($token)
146 $name = substr($token, 2);
148 if (false !== $pos = strpos($name, '=')) {
149 if (0 === \strlen($value = substr($name, $pos + 1))) {
150 // if no value after "=" then substr() returns "" since php7 only, false before
151 // see http://php.net/manual/fr/migration70.incompatible.php#119151
152 if (\PHP_VERSION_ID < 70000 && false === $value) {
155 array_unshift($this->parsed, $value);
157 $this->addLongOption(substr($name, 0, $pos), $value);
159 $this->addLongOption($name, null);
164 * Parses an argument.
166 * @param string $token The current token
168 * @throws RuntimeException When too many arguments are given
170 private function parseArgument($token)
172 $c = \count($this->arguments);
174 // if input is expecting another argument, add it
175 if ($this->definition->hasArgument($c)) {
176 $arg = $this->definition->getArgument($c);
177 $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;
179 // if last argument isArray(), append token to last argument
180 } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
181 $arg = $this->definition->getArgument($c - 1);
182 $this->arguments[$arg->getName()][] = $token;
184 // unexpected argument
186 $all = $this->definition->getArguments();
188 throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))));
191 throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
196 * Adds a short option value.
198 * @param string $shortcut The short option key
199 * @param mixed $value The value for the option
201 * @throws RuntimeException When option given doesn't exist
203 private function addShortOption($shortcut, $value)
205 if (!$this->definition->hasShortcut($shortcut)) {
206 throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
209 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
213 * Adds a long option value.
215 * @param string $name The long option key
216 * @param mixed $value The value for the option
218 * @throws RuntimeException When option given doesn't exist
220 private function addLongOption($name, $value)
222 if (!$this->definition->hasOption($name)) {
223 throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
226 $option = $this->definition->getOption($name);
228 if (null !== $value && !$option->acceptValue()) {
229 throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
232 if (\in_array($value, array('', null), true) && $option->acceptValue() && \count($this->parsed)) {
233 // if option accepts an optional or mandatory argument
234 // let's see if there is one provided
235 $next = array_shift($this->parsed);
236 if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, array('', null), true)) {
239 array_unshift($this->parsed, $next);
243 if (null === $value) {
244 if ($option->isValueRequired()) {
245 throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
248 if (!$option->isArray() && !$option->isValueOptional()) {
253 if ($option->isArray()) {
254 $this->options[$name][] = $value;
256 $this->options[$name] = $value;
263 public function getFirstArgument()
265 foreach ($this->tokens as $token) {
266 if ($token && '-' === $token[0]) {
277 public function hasParameterOption($values, $onlyParams = false)
279 $values = (array) $values;
281 foreach ($this->tokens as $token) {
282 if ($onlyParams && '--' === $token) {
285 foreach ($values as $value) {
286 // Options with values:
287 // For long options, test for '--option=' at beginning
288 // For short options, test for '-o' at beginning
289 $leading = 0 === strpos($value, '--') ? $value.'=' : $value;
290 if ($token === $value || '' !== $leading && 0 === strpos($token, $leading)) {
302 public function getParameterOption($values, $default = false, $onlyParams = false)
304 $values = (array) $values;
305 $tokens = $this->tokens;
307 while (0 < \count($tokens)) {
308 $token = array_shift($tokens);
309 if ($onlyParams && '--' === $token) {
313 foreach ($values as $value) {
314 if ($token === $value) {
315 return array_shift($tokens);
317 // Options with values:
318 // For long options, test for '--option=' at beginning
319 // For short options, test for '-o' at beginning
320 $leading = 0 === strpos($value, '--') ? $value.'=' : $value;
321 if ('' !== $leading && 0 === strpos($token, $leading)) {
322 return substr($token, \strlen($leading));
331 * Returns a stringified representation of the args passed to the command.
335 public function __toString()
337 $tokens = array_map(function ($token) {
338 if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
339 return $match[1].$this->escapeToken($match[2]);
342 if ($token && '-' !== $token[0]) {
343 return $this->escapeToken($token);
349 return implode(' ', $tokens);