2 namespace Drush\Preflight;
4 use Consolidation\SiteAlias\SiteAliasName;
5 use Consolidation\SiteAlias\SiteSpecParser;
8 * Preprocess commandline arguments.
10 * - Record @sitealias, if present
11 * - Record a limited number of global options
13 * Anything not handled here is processed by Symfony Console.
15 class ArgsPreprocessor
17 /** @var SiteSpecParser */
18 protected $specParser;
19 /** @var ArgsRemapper */
23 * ArgsPreprocessor constructor
25 public function __construct()
27 $this->specParser = new SiteSpecParser();
30 public function setArgsRemapper(ArgsRemapper $remapper)
32 $this->remapper = $remapper;
36 * Parse the argv array.
38 * @param string[] $argv
39 * Commandline arguments. The first element is
40 * the path to the application, which we will ignore.
41 * @param PreflightArgsInterface $storage
42 * A storage object to hold the arguments we remove
43 * from argv, plus the remaining argv arguments.
45 public function parse($argv, PreflightArgsInterface $storage)
49 // Pull off the path to application. Add it to the
50 // 'unprocessed' args list.
51 $appName = array_shift($argv);
52 $storage->addArg($appName);
54 if ($this->remapper) {
55 $argv = $this->remapper->remap($argv);
58 $optionsTable = $storage->optionsWithValues();
59 while (!empty($argv)) {
60 $opt = array_shift($argv);
63 $storage->addArg($opt);
64 return $storage->passArgs($argv);
67 if (!$sawArg && !$storage->hasAlias() && $this->isAliasOrSiteSpec($opt)) {
68 $storage->setAlias($opt);
76 list($methodName, $value) = $this->findMethodForOptionWithValues($optionsTable, $opt);
79 $value = array_shift($argv);
81 $method = [$storage, $methodName];
82 call_user_func($method, $value);
84 $storage->addArg($opt);
91 * Determine whether the provided argument is an alias or
92 * a site specification.
98 protected function isAliasOrSiteSpec($arg)
100 if (SiteAliasName::isAliasName($arg)) {
103 return $this->specParser->validSiteSpec($arg);
107 * Check to see if '$opt' is one of the options that we record
108 * that takes a value.
110 * @param $optionsTable Table of option names and the name of the
111 * method that should be called to process that option.
112 * @param $opt The option string to check
113 * @return [$methodName, $optionValue]
115 protected function findMethodForOptionWithValues($optionsTable, $opt)
117 // Skip $opt if it is empty, or if it is not an option.
118 if (empty($opt) || ($opt[0] != '-')) {
119 return [false, false];
122 // Check each entry in the option table in turn; return as soon
123 // as there is a match.
124 foreach ($optionsTable as $key => $methodName) {
125 $result = $this->checkMatchingOption($opt, $key, $methodName);
131 return [false, false];
135 * Check to see if the provided option matches the entry from the
138 * @param $opt The option string to check
139 * @param $key The key to test against. Must always start with '-' or
140 * '--'. If $key ends with '=', then the option must have a value.
141 * Otherwise, it cannot be supplied with a value, and always defaults
143 * @return [$methodName, $optionValue]
145 protected function checkMatchingOption($opt, $keyParam, $methodName)
147 // Test to see if $key ends in '='; remove the character if present.
148 // If the char is removed, it means the option has a value.
149 $key = rtrim($keyParam, '=');
150 $hasValue = $key != $keyParam;
152 // If $opt does not begin with $key, then it cannot be a match.
153 if ($key != substr($opt, 0, strlen($key))) {
154 return [false, false];
157 // If $key and $opt are exact matches, then return a positive result.
158 // The returned $optionValue will be 'null' if the option requires
159 // a value; in this case, the value will be provided from the next
160 // argument in the calling function. If this option does not take a
161 // supplied value, then we set its value to 'true'
162 if (strlen($key) == strlen($opt)) {
163 return [$methodName, $hasValue ? null: true];
166 // If the option is not an exact match for the key, then the next
167 // character in the option after the key name must be an '='. Otherwise,
168 // we might confuse `--locale` for `--local`, etc.
169 if ($opt[strlen($key)] != '=') {
170 return [false, false];
173 // If $opt does not take a value, then we will ignore
174 // of the form --opt=value
176 // TODO: We could fail with "The "--foo" option does not accept a value." here.
177 // It is important that we ignore the value for '--backend', but other options could throw.
178 // For now, we just ignore the value if it is there. This only affects --simulate and --local at the moment.
179 return [$methodName, true];
182 // If $opt is a double-dash option, and it contains an '=', then
183 // the option value is everything after the '='.
184 if ((strlen($key) < strlen($opt)) && ($opt[1] == '-') && ($opt[strlen($key)] == '=')) {
185 $value = substr($opt, strlen($key) + 1);
186 return [$methodName, $value];
189 return [false, false];