* * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ /** * ArgvInput represents an input coming from the CLI arguments. * * Usage: * * $input = new ArgvInput(); * * By default, the `$_SERVER['argv']` array is used for the input values. * * This can be overridden by explicitly passing the input values in the constructor: * * $input = new ArgvInput($_SERVER['argv']); * * If you pass it yourself, don't forget that the first element of the array * is the name of the running program. * * When passing an argument to the constructor, be sure that it respects * the same rules as the argv one. It's almost always better to use the * `StringInput` when you want to provide your own input. * * @author Fabien Potencier * * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 */ class ArgvInput extends Input { protected $tokens; protected $parsed; /** * Constructor. * * @param array $argv An array of parameters from the CLI (in the argv format) * @param InputDefinition $definition A InputDefinition instance */ public function __construct(array $argv = null, InputDefinition $definition = null) { if (null === $argv) { $argv = $_SERVER['argv']; } // strip the program name array_shift($argv); $this->tokens = $argv; parent::__construct($definition); } /** * Processes command line arguments. */ protected function parse() { $this->parsed = $this->tokens; while (null !== $token = array_shift($this->parsed)) { if ('--' === substr($token, 0, 2)) { $this->parseLongOption($token); } elseif ('-' === $token[0]) { $this->parseShortOption($token); } else { $this->parseArgument($token); } } } /** * Parses a short option. * * @param string $token The current token. */ protected function parseShortOption($token) { $name = substr($token, 1); if (strlen($name) > 1) { if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptParameter()) { // an option with a value (with no space) $this->addShortOption($name[0], substr($name, 1)); } else { $this->parseShortOptionSet($name); } } else { $this->addShortOption($name, null); } } /** * Parses a short option set. * * @param string $token The current token * * @throws \RuntimeException When option given doesn't exist */ protected function parseShortOptionSet($name) { $len = strlen($name); for ($i = 0; $i < $len; $i++) { if (!$this->definition->hasShortcut($name[$i])) { throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); } $option = $this->definition->getOptionForShortcut($name[$i]); if ($option->acceptParameter()) { $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); break; } else { $this->addLongOption($option->getName(), true); } } } /** * Parses a long option. * * @param string $token The current token */ protected function parseLongOption($token) { $name = substr($token, 2); if (false !== $pos = strpos($name, '=')) { $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); } else { $this->addLongOption($name, null); } } /** * Parses an argument. * * @param string $token The current token * * @throws \RuntimeException When too many arguments are given */ protected function parseArgument($token) { if (!$this->definition->hasArgument(count($this->arguments))) { throw new \RuntimeException('Too many arguments.'); } $this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token; } /** * Adds a short option value. * * @param string $shortcut The short option key * @param mixed $value The value for the option * * @throws \RuntimeException When option given doesn't exist */ protected function addShortOption($shortcut, $value) { if (!$this->definition->hasShortcut($shortcut)) { throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); } $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); } /** * Adds a long option value. * * @param string $name The long option key * @param mixed $value The value for the option * * @throws \RuntimeException When option given doesn't exist */ protected function addLongOption($name, $value) { if (!$this->definition->hasOption($name)) { throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); } $option = $this->definition->getOption($name); if (null === $value && $option->acceptParameter()) { // if option accepts an optional or mandatory argument // let's see if there is one provided $next = array_shift($this->parsed); if ('-' !== $next[0]) { $value = $next; } else { array_unshift($this->parsed, $next); } } if (null === $value) { if ($option->isParameterRequired()) { throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); } $value = $option->isParameterOptional() ? $option->getDefault() : true; } $this->options[$name] = $value; } /** * Returns the first argument from the raw parameters (not parsed). * * @return string The value of the first argument or null otherwise */ public function getFirstArgument() { foreach ($this->tokens as $token) { if ($token && '-' === $token[0]) { continue; } return $token; } } /** * Returns true if the raw parameters (not parsed) contains a value. * * This method is to be used to introspect the input parameters * before it has been validated. It must be used carefully. * * @param string|array $values The value(s) to look for in the raw parameters (can be an array) * * @return Boolean true if the value is contained in the raw parameters */ public function hasParameterOption($values) { if (!is_array($values)) { $values = array($values); } foreach ($this->tokens as $v) { if (in_array($v, $values)) { return true; } } return false; } }