Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / vendor / drush / drush / src / Preflight / ArgsPreprocessor.php
1 <?php
2 namespace Drush\Preflight;
3
4 use Consolidation\SiteAlias\SiteAliasName;
5 use Consolidation\SiteAlias\SiteSpecParser;
6
7 /**
8  * Preprocess commandline arguments.
9  *
10  * - Record @sitealias, if present
11  * - Record a limited number of global options
12  *
13  * Anything not handled here is processed by Symfony Console.
14  */
15 class ArgsPreprocessor
16 {
17     /** @var SiteSpecParser */
18     protected $specParser;
19     /** @var ArgsRemapper */
20     protected $remapper;
21
22     /**
23      * ArgsPreprocessor constructor
24      */
25     public function __construct()
26     {
27         $this->specParser = new SiteSpecParser();
28     }
29
30     public function setArgsRemapper(ArgsRemapper $remapper)
31     {
32         $this->remapper = $remapper;
33     }
34
35     /**
36      * Parse the argv array.
37      *
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.
44      */
45     public function parse($argv, PreflightArgsInterface $storage)
46     {
47         $sawArg = false;
48
49         // Pull off the path to application. Add it to the
50         // 'unprocessed' args list.
51         $appName = array_shift($argv);
52         $storage->addArg($appName);
53
54         if ($this->remapper) {
55             $argv = $this->remapper->remap($argv);
56         }
57
58         $optionsTable = $storage->optionsWithValues();
59         while (!empty($argv)) {
60             $opt = array_shift($argv);
61
62             if ($opt == '--') {
63                 $storage->addArg($opt);
64                 return $storage->passArgs($argv);
65             }
66
67             if (!$sawArg && !$storage->hasAlias() && $this->isAliasOrSiteSpec($opt)) {
68                 $storage->setAlias($opt);
69                 continue;
70             }
71
72             if ($opt[0] != '-') {
73                 $sawArg = true;
74             }
75
76             list($methodName, $value) = $this->findMethodForOptionWithValues($optionsTable, $opt);
77             if ($methodName) {
78                 if (!isset($value)) {
79                     $value = array_shift($argv);
80                 }
81                 $method = [$storage, $methodName];
82                 call_user_func($method, $value);
83             } else {
84                 $storage->addArg($opt);
85             }
86         }
87         return $storage;
88     }
89
90     /**
91      * Determine whether the provided argument is an alias or
92      * a site specification.
93      *
94      * @param string $arg
95      *   Argument to test.
96      * @return bool
97      */
98     protected function isAliasOrSiteSpec($arg)
99     {
100         if (SiteAliasName::isAliasName($arg)) {
101             return true;
102         }
103         return $this->specParser->validSiteSpec($arg);
104     }
105
106     /**
107      * Check to see if '$opt' is one of the options that we record
108      * that takes a value.
109      *
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]
114      */
115     protected function findMethodForOptionWithValues($optionsTable, $opt)
116     {
117         // Skip $opt if it is empty, or if it is not an option.
118         if (empty($opt) || ($opt[0] != '-')) {
119             return [false, false];
120         }
121
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);
126             if ($result[0]) {
127                 return $result;
128             }
129         }
130
131         return [false, false];
132     }
133
134     /**
135      * Check to see if the provided option matches the entry from the
136      * option table.
137      *
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
142      *   to 'true'.
143      * @return [$methodName, $optionValue]
144      */
145     protected function checkMatchingOption($opt, $keyParam, $methodName)
146     {
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;
151
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];
155         }
156
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];
164         }
165
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];
171         }
172
173         // If $opt does not take a value, then we will ignore
174         // of the form --opt=value
175         if (!$hasValue) {
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];
180         }
181
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];
187         }
188
189         return [false, false];
190     }
191 }