ArgvInput.php 7.36 KB
Newer Older
1 2
<?php

3
namespace Symfony\Component\Console\Input;
4 5

/*
6
 * This file is part of the Symfony framework.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * 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.
 *
23
 * This can be overridden by explicitly passing the input values in the constructor:
24 25 26 27 28 29 30 31 32 33
 *
 *     $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.
 *
34
 * @author Fabien Potencier <fabien.potencier@symfony-project.com>
35 36 37 38 39 40
 *
 * @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
{
41 42 43 44 45 46 47 48 49 50
    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)
51
    {
52 53 54
        if (null === $argv) {
            $argv = $_SERVER['argv'];
        }
55

56 57
        // strip the program name
        array_shift($argv);
58

59
        $this->tokens = $argv;
60

61 62
        parent::__construct($definition);
    }
63

64 65 66 67
    /**
     * Processes command line arguments.
     */
    protected function parse()
68
    {
69 70 71 72 73 74 75 76 77 78
        $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);
            }
        }
79 80
    }

81 82 83 84 85 86
    /**
     * Parses a short option.
     *
     * @param string $token The current token.
     */
    protected function parseShortOption($token)
87
    {
88 89 90
        $name = substr($token, 1);

        if (strlen($name) > 1) {
91
            if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
92 93 94 95 96 97 98 99
                // an option with a value (with no space)
                $this->addShortOption($name[0], substr($name, 1));
            } else {
                $this->parseShortOptionSet($name);
            }
        } else {
            $this->addShortOption($name, null);
        }
100
    }
101 102 103 104 105 106 107 108 109

    /**
     * Parses a short option set.
     *
     * @param string $token The current token
     *
     * @throws \RuntimeException When option given doesn't exist
     */
    protected function parseShortOptionSet($name)
110
    {
111 112 113 114 115 116 117
        $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]);
118
            if ($option->acceptValue()) {
119 120 121 122 123 124 125
                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));

                break;
            } else {
                $this->addLongOption($option->getName(), true);
            }
        }
126 127
    }

128 129 130 131 132 133
    /**
     * Parses a long option.
     *
     * @param string $token The current token
     */
    protected function parseLongOption($token)
134
    {
135
        $name = substr($token, 2);
136

137 138 139 140 141
        if (false !== $pos = strpos($name, '=')) {
            $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
        } else {
            $this->addLongOption($name, null);
        }
142 143
    }

144 145 146 147 148 149 150 151
    /**
     * Parses an argument.
     *
     * @param string $token The current token
     *
     * @throws \RuntimeException When too many arguments are given
     */
    protected function parseArgument($token)
152
    {
153 154 155
        if (!$this->definition->hasArgument(count($this->arguments))) {
            throw new \RuntimeException('Too many arguments.');
        }
156

157
        $this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token;
158 159
    }

160 161 162 163 164 165 166 167 168
    /**
     * 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)
169
    {
170 171 172
        if (!$this->definition->hasShortcut($shortcut)) {
            throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
        }
173

174
        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
175 176
    }

177 178 179 180 181 182 183 184 185
    /**
     * 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)
186
    {
187 188 189 190 191 192
        if (!$this->definition->hasOption($name)) {
            throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
        }

        $option = $this->definition->getOption($name);

193
        if (null === $value && $option->acceptValue()) {
194 195 196 197 198 199 200 201 202 203 204
            // 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) {
205
            if ($option->isValueRequired()) {
206 207 208
                throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
            }

209
            $value = $option->isValueOptional() ? $option->getDefault() : true;
210 211 212
        }

        $this->options[$name] = $value;
213 214
    }

215 216 217 218 219 220
    /**
     * Returns the first argument from the raw parameters (not parsed).
     *
     * @return string The value of the first argument or null otherwise
     */
    public function getFirstArgument()
221
    {
222 223 224 225
        foreach ($this->tokens as $token) {
            if ($token && '-' === $token[0]) {
                continue;
            }
226

227 228
            return $token;
        }
229 230
    }

231 232 233 234 235 236 237 238 239 240 241
    /**
     * 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)
242
    {
243 244 245
        if (!is_array($values)) {
            $values = array($values);
        }
246

247 248 249 250 251
        foreach ($this->tokens as $v) {
            if (in_array($v, $values)) {
                return true;
            }
        }
252

253
        return false;
254 255
    }
}