Patched to Drupal 8.4.8 level. See https://www.drupal.org/sa-core-2018-004 and patch...
[yaffs-website] / vendor / drush / drush / includes / startup.inc
1 <?php
2
3 /**
4  * @file
5  *   Functions used when Drush is starting up.
6  *
7  * This file is included and used early in the Drush
8  * startup process, before any other Drush APIs are
9  * available, and before the autoloader has been included.
10  */
11
12 /**
13  * Get the current enviornment.
14  */
15 function drush_env() {
16   // Fetch the current environment.  To ensure that
17   // $_ENV is correctly populated, make sure that
18   // the value of 'variables-order' in your php.ini
19   // contains "E" ("Environment").  See:
20   // http://us.php.net/manual/en/ini.core.php#ini.variables-order
21   $env = $_ENV;
22
23   // If PHP is not configured correctly, $_ENV will be
24   // empty.  Drush counts on the fact that environment
25   // variables will always be available, though, so we
26   // need to repair this situation.  We can always access
27   // individual environmnet values via getenv(); however,
28   // there is no PHP API that will tell us all of the
29   // available values, so we will get the environment
30   // variable values using 'printenv'.
31   if (empty($env)) {
32     exec('printenv', $env_items);
33     foreach ($env_items as $item) {
34       // Each $item is 'key=value' or just 'key'.
35       // If $item has no value, then explode will return
36       // a single array, [0 => 'key'].  We add a default
37       // value of [1 => 'value'] to cover this case.  If
38       // explode returns two items, the default value is ignored.
39       list($key, $value) = explode('=', $item, 2) + array(1 => '');
40       $env[$key] = $value;
41     }
42   }
43
44   return $env;
45 }
46
47 /**
48  * Checks the provided location and return the appropriate
49  * Drush wrapper or Drush launcher script, if found.
50  *
51  * If the provided location looks like it might be a web
52  * root (i.e., it contains an index.php), then we will search
53  * in a number of locations in the general vicinity of the
54  * web root for a Drush executable.
55  *
56  * For other locations, we will look only in that specific
57  * directory, or in vendor/bin.
58  */
59 function find_wrapper_or_launcher($location) {
60   if (file_exists($location. DIRECTORY_SEPARATOR. 'index.php')) {
61     return find_wrapper_or_launcher_in_vicinity($location);
62   }
63   return find_wrapper_or_launcher_in_specific_locations($location, ["", 'vendor'. DIRECTORY_SEPARATOR. 'bin']);
64 }
65
66 /**
67  * We look for a "Drush wrapper" script that might
68  * be stored in the root of a site.  If there is
69  * no wrapper script, then we look for the
70  * drush.launcher script in vendor/bin.  We try just a
71  * few of the most common locations; if the user relocates
72  * their vendor directory anywhere else, then they must
73  * use a wrapper script to locate it.  See the comment in
74  * 'examples/drush' for details.
75  */
76 function find_wrapper_or_launcher_in_vicinity($location) {
77   $sep = DIRECTORY_SEPARATOR;
78   $drush_locations = [
79     "",
80     "vendor{$sep}bin/",
81     "..{$sep}vendor{$sep}bin{$sep}",
82     "sites{$sep}all{$sep}vendor{$sep}bin{$sep}",
83     "sites{$sep}all{$sep}vendor{$sep}drush{$sep}drush{$sep}",
84     "sites{$sep}all{$sep}drush{$sep}drush{$sep}",
85     "drush{$sep}drush{$sep}",
86   ];
87
88   return find_wrapper_or_launcher_in_specific_locations($location, $drush_locations);
89 }
90
91 function find_wrapper_or_launcher_in_specific_locations($location, $drush_locations) {
92   $sep = DIRECTORY_SEPARATOR;
93   foreach ($drush_locations as $d) {
94     $found_script = find_wrapper_or_launcher_at_location("$location$sep$d");
95     if (!empty($found_script)) {
96       return $found_script;
97     }
98   }
99   return "";
100 }
101
102 /**
103  * We are somewhat "loose" about whether we are looking
104  * for "drush" or "drush.launcher", because in old versions
105  * of Drush, the "drush launcher" was named "drush".
106  * Otherwise, there wouldn't be any point in looking for
107  * "drush.launcher" at the root, or "drush" in a vendor directory.
108  * We also allow users to rename their drush wrapper to
109  * 'drush.wrapper' to avoid conflicting with a directory named
110  * 'drush' at the site root.
111  */
112 function find_wrapper_or_launcher_at_location($location) {
113   $sep = DIRECTORY_SEPARATOR;
114   // Sanity-check: empty $location means that we should search
115   // at the cwd, not at the root of the filesystem.
116   if (empty($location)) {
117     $location = ".";
118   }
119   foreach (array('.launcher', '.wrapper', '') as $suffix) {
120     $check_location = "$location{$sep}drush$suffix";
121     if (is_file($check_location)) {
122       return $check_location;
123     }
124   }
125   return "";
126 }
127
128 /**
129  * Determine whether current OS is a Windows variant.
130  */
131 function drush_is_windows($os = NULL) {
132   // The _drush_get_os() function may not be available, so resolve "LOCAL"
133   if (!$os || $os == "LOCAL") {
134     $os = PHP_OS;
135   }
136   return strtoupper(substr($os, 0, 3)) === 'WIN';
137 }
138
139 function drush_escapeshellarg($arg, $os = NULL, $raw = FALSE) {
140   // Short-circuit escaping for simple params (keep stuff readable)
141   if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
142     return $arg;
143   }
144   elseif (drush_is_windows($os)) {
145     return _drush_escapeshellarg_windows($arg, $raw);
146   }
147   else {
148     return _drush_escapeshellarg_linux($arg, $raw);
149   }
150 }
151
152 /**
153  * Linux version of escapeshellarg().
154  *
155  * This is intended to work the same way that escapeshellarg() does on
156  * Linux.  If we need to escape a string that will be used remotely on
157  * a Linux system, then we need our own implementation of escapeshellarg,
158  * because the Windows version behaves differently.
159  */
160 function _drush_escapeshellarg_linux($arg, $raw = FALSE) {
161   // For single quotes existing in the string, we will "exit"
162   // single-quote mode, add a \' and then "re-enter"
163   // single-quote mode.  The result of this is that
164   // 'quote' becomes '\''quote'\''
165   $arg = preg_replace('/\'/', '\'\\\'\'', $arg);
166
167   // Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
168   // Note that this replacement makes Drush's escapeshellarg work differently
169   // than the built-in escapeshellarg in PHP on Linux, as these characters
170   // usually are NOT replaced. However, this was done deliberately to be more
171   // conservative when running _drush_escapeshellarg_linux on Windows
172   // (this can happen when generating a command to run on a remote Linux server.)
173   $arg = str_replace(array("\t", "\n", "\r", "\0", "\x0B"), ' ', $arg);
174
175   // Only wrap with quotes when needed.
176   if(!$raw) {
177     // Add surrounding quotes.
178     $arg = "'" . $arg . "'";
179   }
180
181   return $arg;
182 }
183
184 /**
185  * Windows version of escapeshellarg().
186  */
187 function _drush_escapeshellarg_windows($arg, $raw = FALSE) {
188   // Double up existing backslashes
189   $arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
190
191   // Double up double quotes
192   $arg = preg_replace('/"/', '""', $arg);
193
194   // Double up percents.
195   // $arg = preg_replace('/%/', '%%', $arg);
196
197   // Only wrap with quotes when needed.
198   if(!$raw) {
199     // Add surrounding quotes.
200     $arg = '"' . $arg . '"';
201   }
202
203   return $arg;
204 }
205
206 /**
207  * drush_startup is called once, by the Drush "finder"
208  * script -- the "drush" script at the Drush root.
209  * It finds the correct Drush "wrapper" or "launcher"
210  * script to use, and executes it with process replacement.
211  */
212 function drush_startup($argv) {
213   $sep = DIRECTORY_SEPARATOR;
214   $found_script = "";
215   $cwd = getcwd();
216   $home = getenv("HOME");
217   $use_dir = "$home{$sep}.drush{$sep}use";
218
219   // Get the arguments for the command.  Shift off argv[0],
220   // which contains the name of this script.
221   $arguments = $argv;
222   array_shift($arguments);
223
224   // We need to do at least a partial parsing of the options,
225   // so that we can find --root / -r and so on.
226   $VERBOSE=FALSE;
227   $DEBUG=FALSE;
228   $ROOT=FALSE;
229   $COMMAND=FALSE;
230   $ALIAS=FALSE;
231   $VAR=FALSE;
232
233   foreach ($arguments as $arg) {
234     // If a variable to set was indicated on the
235     // previous iteration, then set the value of
236     // the named variable (e.g. "ROOT") to "$arg".
237     if ($VAR) {
238       $$VAR = "$arg";
239       $VAR = FALSE;
240     }
241     else {
242       switch ($arg) {
243         case "-r":
244           $VAR = "ROOT";
245           break;
246
247         case "-dv":
248         case "-vd":
249         case "--debug":
250         case "-d":
251           $DEBUG = TRUE;
252           break;
253
254         case "-dv":
255         case "-vd":
256         case "--verbose":
257         case "-v":
258           $VERBOSE = TRUE;
259           break;
260       }
261       if (!$COMMAND && !$ALIAS && ($arg[0] == '@')) {
262         $ALIAS = $arg;
263       }
264       elseif (!$COMMAND && ($arg[0] != '-')) {
265         $COMMAND = $arg;
266       }
267       if (substr($arg, 0, 7) == "--root=") {
268         $ROOT = substr($arg, 7);
269       }
270     }
271   }
272
273   $NONE=($ALIAS == "@none");
274
275   // If we have found the site-local Drush script, then
276   // do not search for it again; use the environment value
277   // we set last time.
278   $found_script = getenv('DRUSH_FINDER_SCRIPT');
279
280   // If the @none alias is used, then we skip the Drush wrapper,
281   // and call the Drush launcher directly.
282   //
283   // In this instance, we are assuming that the 'drush' that is being
284   // called is:
285   //
286   //  a) The global 'drush', or
287   //  b) A site-local 'drush' in a vendor/bin directory.
288   //
289   // In either event, the appropriate 'drush.launcher' should be right next
290   // to this script (stored in the same directory).
291   if (empty($found_script) && $NONE) {
292     if (is_file(dirname(__DIR__) . "{$sep}drush.launcher")) {
293       $found_script = dirname(__DIR__) . "{$sep}drush.launcher";
294     }
295     else {
296       fwrite(STDERR, "Could not find drush.launcher in " . dirname(__DIR__) . ". Check your installation.\n");
297       exit(1);
298     }
299   }
300
301   // Check for a root option:
302   //
303   //   drush --root=/path
304   //
305   // If the site root is specified via a commandline option, then we
306   // should always use the Drush stored at this root, if there is one.
307   // We will first check for a "wrapper" script at the root, and then
308   // we will look for a "launcher" script in vendor/bin.
309   if (empty($found_script) && !empty($ROOT)) {
310     $found_script = find_wrapper_or_launcher($ROOT);
311     if (!empty($found_script)) {
312       chdir($ROOT);
313     }
314   }
315
316   // If there is a .drush-use file, then its contents will
317   // contain the path to the Drush to use.
318   if (empty($found_script)) {
319     if (is_file(".drush-use")) {
320       $found_script = trim(file_get_contents(".drush-use"));
321     }
322   }
323
324   // Look for a 'drush' wrapper or launcher at the cwd,
325   // and in each of the directories above the cwd.  If
326   // we find one, use it.
327   if (empty($found_script)) {
328     $c = getcwd();
329     // Windows can give us lots of different strings to represent the root
330     // directory as it often includes the drive letter. If we get the same
331     // result from dirname() twice in a row, then we know we're at the root.
332     $last = '';
333     while (!empty($c) && ($c != $last)) {
334       $found_script = find_wrapper_or_launcher($c);
335       if ($found_script) {
336         chdir($c);
337         break;
338       }
339       $last = $c;
340       $c = dirname($c);
341     }
342   }
343
344   if (!empty($found_script)) {
345     $found_script = realpath($found_script);
346
347     // Guard against errors:  if we have found a "drush" script
348     // (that is, theoretically a drush wrapper script), and
349     // there is a "drush.launcher" script in the same directory,
350     // then we will skip the "drush" script and use the drush launcher
351     // instead.  This is because drush "wrapper" scripts should
352     // only ever exist at the root of a site, and there should
353     // never be a drush "launcher" at the root of a site.
354     // Therefore, if we find a "drush.launcher" next to a script
355     // called "drush", we have probably found a Drush install directory,
356     // not a site root.  Adjust appropriately.  Note that this
357     // also catches the case where a drush "finder" script finds itself.
358     if (is_file(dirname($found_script) . "{$sep}drush.launcher")) {
359       $found_script = dirname($found_script) . "{$sep}drush.launcher";
360     }
361   }
362
363   // Didn't find any site-local Drush, or @use'd Drush.
364   // Skip the Bash niceties of the launcher and proceed to drush_main() in either case:
365   // - No script was found and we are running a Phar
366   // - The found script *is* the Phar https://github.com/drush-ops/drush/pull/2246.
367   $phar_path = class_exists('Phar') ? Phar::running(FALSE) : '';
368   if ((empty($found_script) && $phar_path) || !empty($found_script) && $found_script == $phar_path) {
369     drush_run_main($DEBUG, $sep, "Phar detected. Proceeding to drush_main().");
370   }
371
372   // Didn't find any site-local Drush, or @use'd Drush, or Phar.
373   // There should be a drush.launcher in same directory as this script.
374   if (empty($found_script)) {
375     $found_script = dirname(__DIR__) . "{$sep}drush.launcher";
376   }
377
378   if (drush_is_windows()) {
379     // Sometimes we found launcher in /bin, and sometimes not. Adjust accordingly.
380     if (strpos($found_script, 'bin')) {
381       $found_script = dirname($found_script). $sep. 'drush.php.bat';
382     }
383     else {
384       array_unshift($arguments, dirname($found_script). $sep. 'drush.php');
385       $found_script = 'php';
386     }
387   }
388
389   // Always use pcntl_exec if it exists.
390   $use_pcntl_exec = function_exists("pcntl_exec") && (strpos(ini_get('disable_functions'), 'pcntl_exec') === FALSE);
391
392   // If we have posix_getppid, then pass in the shell pid so
393   // that 'site-set' et. al. can work correctly.
394   if (function_exists('posix_getppid')) {
395     putenv("DRUSH_SHELL_PID=" . posix_getppid());
396   }
397
398   // Set an environment variable indicating which script
399   // the Drush finder found. If we end up re-entrantly calling
400   // another Drush finder, then we will skip searching for
401   // a site-local Drush, and always use the drush.launcher
402   // found previously.  This environment variable typically should
403   // not be set by clients.
404   putenv("DRUSH_FINDER_SCRIPT=$found_script");
405
406   // Emit a message in debug mode advertising the location of the
407   // script we found.
408   if ($DEBUG) {
409     $launch_method = $use_pcntl_exec ? 'pcntl_exec' : 'proc_open';
410     fwrite(STDERR, "Using the Drush script found at $found_script using $launch_method\n");
411   }
412
413   if ($use_pcntl_exec) {
414     // Get the current environment for pnctl_exec.
415     $env = drush_env();
416
417     // Launch the new script in the same process.
418     // If the launch succeeds, then it will not return.
419     $error = pcntl_exec($found_script, $arguments, $env);
420     if (!$error) {
421       $errno = pcntl_get_last_error();
422       $strerror = pcntl_strerror($errno);
423       fwrite(STDERR, "Error has occurred executing the Drush script found at $found_script\n");
424       fwrite(STDERR, "(errno {$errno}) $strerror\n");
425     }
426     exit(1);
427   }
428   else {
429     $escaped_args = array_map(function($item) { return drush_escapeshellarg($item); }, $arguments);
430     // Double quotes around $found_script as it can contain spaces.
431     $cmd = drush_escapeshellarg($found_script). ' '. implode(' ', $escaped_args);
432     if (drush_is_windows()) {
433       // Windows requires double quotes around whole command.
434       // @see https://bugs.php.net/bug.php?id=49139
435       // @see https://bugs.php.net/bug.php?id=60181
436       $cmd = '"'. $cmd. '"';
437     }
438     $process = proc_open($cmd, array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes, $cwd);
439     $proc_status = proc_get_status($process);
440     $exit_code = proc_close($process);
441     exit($proc_status["running"] ? $exit_code : $proc_status["exitcode"] );
442   }
443 }
444
445 /**
446  * Run drush_main() and then exit. Used when we cannot hand over execution to
447  * the launcher.
448  *
449  * @param bool $DEBUG
450  *   Are we in debug mode
451  * @param string $sep
452  *   Directory separator
453  * @param string $msg
454  *   Debug message to log before running drush_main()
455  */
456 function drush_run_main($DEBUG, $sep, $msg) {
457 // Emit a message in debug mode advertising how we proceeded.
458   if ($DEBUG) {
459     fwrite(STDERR, $msg. "\n");
460   }
461   require __DIR__ . "{$sep}preflight.inc";
462   exit(drush_main());
463 }