Security update for Core, with self-updated composer
[yaffs-website] / vendor / drush / drush / includes / environment.inc
1 <?php
2
3 /**
4  * @file
5  * Functions used by drush to query the environment and
6  * setting the current configuration.
7  *
8  * Bootstrapping now occurs in bootstrap.inc.
9  *
10  * @see includes/bootstrap.inc
11  */
12
13 use Drush\Log\LogLevel;
14 use Webmozart\PathUtil\Path;
15
16 /**
17  * Log PHP errors to the Drush log. This is in effect until Drupal's error
18  * handler takes over.
19  */
20 function drush_error_handler($errno, $message, $filename, $line, $context) {
21   // E_DEPRECATED was added in PHP 5.3. Drupal 6 will not fix all the
22   // deprecated errors, but suppresses them. So we suppress them as well.
23   if (defined('E_DEPRECATED')) {
24     $errno = $errno & ~E_DEPRECATED;
25   }
26
27   // "error_reporting" is usually set in php.ini, but may be changed by
28   // drush_errors_on() and drush_errors_off().
29   if ($errno & error_reporting()) {
30     // By default we log notices.
31     $type = drush_get_option('php-notices', 'notice');
32     $halt_on_error = drush_get_option('halt-on-error', (drush_drupal_major_version() != 6));
33
34     // Bitmask value that constitutes an error needing to be logged.
35     $error = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
36     if ($errno & $error) {
37       $type = 'error';
38     }
39
40     // Bitmask value that constitutes a warning being logged.
41     $warning = E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING;
42     if ($errno & $warning) {
43       $type = LogLevel::WARNING;
44     }
45
46     drush_log($message . ' ' . basename($filename) . ':' . $line, $type);
47
48     if ($errno == E_RECOVERABLE_ERROR && $halt_on_error) {
49       drush_log(dt('E_RECOVERABLE_ERROR encountered; aborting. To ignore recoverable errors, run again with --no-halt-on-error'), 'error');
50       exit(DRUSH_APPLICATION_ERROR);
51     }
52
53     return TRUE;
54   }
55 }
56
57 /**
58  * Returns a localizable message about php.ini that
59  * varies depending on whether the php_ini_loaded_file()
60  * is available or not.
61  */
62 function _drush_php_ini_loaded_file_message() {
63   if (function_exists('php_ini_loaded_file')) {
64     return dt('Please check your configuration settings in !phpini or in your drush.ini file; see examples/example.drush.ini for details.', array('!phpini' => php_ini_loaded_file()));
65   }
66   else {
67     return dt('Please check your configuration settings in your php.ini file or in your drush.ini file; see examples/example.drush.ini for details.');
68   }
69 }
70
71 /**
72  * Evalute the environment after an abnormal termination and
73  * see if we can determine any configuration settings that the user might
74  * want to adjust.
75  */
76 function _drush_postmortem() {
77   // Make sure that the memory limit has been bumped up from the minimum default value of 32M.
78   $php_memory_limit = drush_memory_limit();
79   if (($php_memory_limit > 0) && ($php_memory_limit <= 32*DRUSH_KILOBYTE*DRUSH_KILOBYTE)) {
80     drush_set_error('DRUSH_MEMORY_LIMIT', dt('Your memory limit is set to !memory_limit; Drush needs as much memory to run as Drupal.  !php_ini_msg', array('!memory_limit' => $php_memory_limit / (DRUSH_KILOBYTE*DRUSH_KILOBYTE) . 'M', '!php_ini_msg' => _drush_php_ini_loaded_file_message())));
81   }
82 }
83
84 /**
85  * Evaluate the environment before command bootstrapping
86  * begins.  If the php environment is too restrictive, then
87  * notify the user that a setting change is needed and abort.
88  */
89 function _drush_environment_check_php_ini() {
90   $ini_checks = array('safe_mode' => '', 'open_basedir' => '', 'disable_functions' => array('exec', 'system'), 'disable_classes' => '');
91
92   // Test to insure that certain php ini restrictions have not been enabled
93   $prohibited_list = array();
94   foreach ($ini_checks as $prohibited_mode => $disallowed_value) {
95     $ini_value = ini_get($prohibited_mode);
96     $invalid_value = FALSE;
97     if (empty($disallowed_value)) {
98       $invalid_value = !empty($ini_value) && (strcasecmp($ini_value, 'off') != 0);
99     }
100     else {
101       foreach ($disallowed_value as $test_value) {
102         if (preg_match('/(^|,)' . $test_value . '(,|$)/', $ini_value)) {
103           $invalid_value = TRUE;
104         }
105       }
106     }
107     if ($invalid_value) {
108       $prohibited_list[] = $prohibited_mode;
109     }
110   }
111   if (!empty($prohibited_list)) {
112     drush_log(dt('The following restricted PHP modes have non-empty values: !prohibited_list. This configuration is incompatible with drush.  !php_ini_msg', array('!prohibited_list' => implode(' and ', $prohibited_list), '!php_ini_msg' => _drush_php_ini_loaded_file_message())), LogLevel::ERROR);
113   }
114
115   return TRUE;
116 }
117
118 /**
119  * Returns the current working directory.
120  *
121  * This is the directory as it was when drush was started, not the
122  * directory we are currently in. For that, use getcwd() directly.
123  */
124 function drush_cwd() {
125   if ($path = drush_get_context('DRUSH_OLDCWD')) {
126     return $path;
127   }
128   // We use PWD if available because getcwd() resolves symlinks, which
129   // could take us outside of the Drupal root, making it impossible to find.
130   // $_SERVER['PWD'] isn't set on windows and generates a Notice.
131   $path = isset($_SERVER['PWD']) ? $_SERVER['PWD'] : '';
132   if (empty($path)) {
133     $path = getcwd();
134   }
135
136   // Convert windows paths.
137   $path = Path::canonicalize($path);
138
139   // Save original working dir case some command wants it.
140   drush_set_context('DRUSH_OLDCWD', $path);
141
142   return $path;
143 }
144
145 /**
146  * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3).
147  * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a
148  * proper drive path, still with Unix slashes (c:/dir1).
149  */
150 function _drush_convert_path($path) {
151   $path = str_replace('\\','/', $path);
152   if (drush_is_windows(_drush_get_os()) && !drush_is_cygwin(_drush_get_os())) {
153     $path = preg_replace('/^\/cygdrive\/([A-Za-z])(.*)$/', '\1:\2', $path);
154   }
155
156   return $path;
157 }
158
159 /**
160  * Returns parent directory.
161  *
162  * @param string
163  *   Path to start from.
164  *
165  * @return string
166  *   Parent path of given path.
167  */
168 function _drush_shift_path_up($path) {
169   if (empty($path)) {
170     return FALSE;
171   }
172   $path = explode(DIRECTORY_SEPARATOR, $path);
173   // Move one directory up.
174   array_pop($path);
175   return implode(DIRECTORY_SEPARATOR, $path);
176   // return dirname($path);
177 }
178
179 /**
180  * Like Drupal conf_path, but searching from beneath.
181  * Allows proper site uri detection in site sub-directories.
182  *
183  * Essentially looks for a settings.php file.  Drush uses this
184  * function to find a usable site based on the user's current
185  * working directory.
186  *
187  * @param string
188  *   Search starting path. Defaults to current working directory.
189  *
190  * @return
191  *   Current site path (folder containing settings.php) or FALSE if not found.
192  */
193 function drush_site_path($path = NULL) {
194   $site_path = FALSE;
195
196   $path = empty($path) ? drush_cwd() : $path;
197   // Check the current path.
198   if (file_exists($path . '/settings.php')) {
199     $site_path = $path;
200   }
201   else {
202     // Move up dir by dir and check each.
203     // Stop if we get to a Drupal root.   We don't care
204     // if it is DRUSH_SELECTED_DRUPAL_ROOT or some other root.
205     while (($path = _drush_shift_path_up($path)) && !drush_valid_root($path)) {
206       if (file_exists($path . '/settings.php')) {
207         $site_path = $path;
208         break;
209       }
210     }
211   }
212
213   $site_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
214   if (file_exists($site_root . '/sites/sites.php')) {
215     $sites = array();
216     // This will overwrite $sites with the desired mappings.
217     include($site_root . '/sites/sites.php');
218     // We do a reverse lookup here to determine the URL given the site key.
219     if ($match = array_search($site_path, $sites)) {
220       $site_path = $match;
221     }
222   }
223
224   // Last resort: try from site root
225   if (!$site_path) {
226     if ($site_root) {
227       if (file_exists($site_root . '/sites/default/settings.php')) {
228         $site_path = $site_root . '/sites/default';
229       }
230     }
231   }
232
233   return $site_path;
234 }
235
236 /**
237  * Lookup a site's directory via the sites.php file given a hostname.
238  *
239  * @param $hostname
240  *   The hostname of a site.  May be converted from URI.
241  *
242  * @return $dir
243  *   The directory associated with that hostname or FALSE if not found.
244  */
245 function drush_site_dir_lookup_from_hostname($hostname, $site_root = NULL) {
246   if (!isset($site_root)) {
247     $site_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
248   }
249   if (!empty($site_root) && file_exists($site_root . '/sites/sites.php')) {
250     $sites = array();
251     // This will overwrite $sites with the desired mappings.
252     include($site_root . '/sites/sites.php');
253     return isset($sites[$hostname]) ? $sites[$hostname] : FALSE;
254   }
255   else {
256     return FALSE;
257   }
258 }
259
260 /**
261  * This is a copy of Drupal's conf_path function, taken from D7 and
262  * adjusted slightly to search from the selected Drupal Root.
263  *
264  * Drush uses this routine to find a usable site based on a URI
265  * passed in via a site alias record or the --uri commandline option.
266  *
267  * Drush uses Drupal itself (specifically, the Drupal conf_path function)
268  * to bootstrap the site itself.  If the implementation of conf_path
269  * changes, the site should still bootstrap correctly; the only consequence
270  * of this routine not working is that drush configuration files
271  * (drushrc.php) stored with the site's drush folders might not be found.
272  */
273 function drush_conf_path($server_uri, $require_settings = TRUE) {
274   $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
275   if(empty($drupal_root) || empty($server_uri)) {
276     return NULL;
277   }
278   $parsed_uri = parse_url($server_uri);
279   if (is_array($parsed_uri) && !array_key_exists('scheme', $parsed_uri)) {
280     $parsed_uri = parse_url('http://' . $server_uri);
281   }
282   if (!is_array($parsed_uri)) {
283     return NULL;
284   }
285   $server_host = $parsed_uri['host'];
286   if (array_key_exists('path', $parsed_uri)) {
287     $server_uri = $parsed_uri['path'] . '/index.php';
288   }
289   else {
290     $server_uri = "/index.php";
291   }
292   $confdir = 'sites';
293
294   $sites = array();
295   if (file_exists($drupal_root . '/' . $confdir . '/sites.php')) {
296     // This will overwrite $sites with the desired mappings.
297     include($drupal_root . '/' . $confdir . '/sites.php');
298   }
299
300   $uri = explode('/', $server_uri);
301   $server = explode('.', implode('.', array_reverse(explode(':', rtrim($server_host, '.')))));
302   for ($i = count($uri) - 1; $i > 0; $i--) {
303     for ($j = count($server); $j > 0; $j--) {
304       $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
305       if (isset($sites[$dir]) && file_exists($drupal_root . '/' . $confdir . '/' . $sites[$dir])) {
306         $dir = $sites[$dir];
307       }
308       if (file_exists($drupal_root . '/' . $confdir . '/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir))) {
309         $conf = "$confdir/$dir";
310         return $conf;
311       }
312     }
313   }
314   $conf = "$confdir/default";
315   return $conf;
316 }
317
318 /**
319  * Exhaustive depth-first search to try and locate the Drupal root directory.
320  * This makes it possible to run Drush from a subdirectory of the drupal root.
321  *
322  * @param
323  *   Search start path. Defaults to current working directory.
324  * @return
325  *   A path to drupal root, or FALSE if not found.
326  */
327 function drush_locate_root($start_path = NULL) {
328   $drupal_root = FALSE;
329
330   $start_path = empty($start_path) ? drush_cwd() : $start_path;
331   foreach (array(TRUE, FALSE) as $follow_symlinks) {
332     $path = $start_path;
333     if ($follow_symlinks && is_link($path)) {
334       $path = realpath($path);
335     }
336     // Check the start path.
337     if (drush_valid_root($path)) {
338       $drupal_root = $path;
339       break;
340     }
341     else {
342       // Move up dir by dir and check each.
343       while ($path = _drush_shift_path_up($path)) {
344         if ($follow_symlinks && is_link($path)) {
345           $path = realpath($path);
346         }
347         if (drush_valid_root($path)) {
348           $drupal_root = $path;
349           break 2;
350         }
351       }
352     }
353   }
354
355   return $drupal_root;
356 }
357
358 /**
359  * Checks whether given path qualifies as a Drupal root.
360  *
361  * @param string
362  *   Path to check.
363  *
364  * @return string
365  *   The relative path to common.inc (varies by Drupal version), or FALSE if not
366  *   a Drupal root.
367  */
368 function drush_valid_root($path) {
369   $bootstrap_class = drush_bootstrap_class_for_root($path);
370   return $bootstrap_class != NULL;
371 }
372
373 /**
374  * Tests the currently loaded database credentials to ensure a database connection can be made.
375  *
376  * @param bool $log_errors
377  *   (optional) If TRUE, log error conditions; otherwise be quiet.
378  *
379  * @return bool
380  *   TRUE if database credentials are valid.
381  */
382 function drush_valid_db_credentials() {
383   try {
384     $sql = drush_sql_get_class();
385     if (!$sqlVersion = drush_sql_get_version()) {
386       drush_log(dt('While checking DB credentials, could not instantiate SQLVersion class.'), 'debug');
387       return FALSE;
388     }
389     if (!$sqlVersion->valid_credentials($sql->db_spec())) {
390       drush_log(dt('DB credentials are invalid.'), 'debug');
391       return FALSE;
392     }
393     return $sql->query('SELECT 1;');
394   }
395   catch (Exception $e) {
396     drush_log(dt('Checking DB credentials yielded error: @e', array('@e' => $e->getMessage())), 'debug');
397     return FALSE;
398   }
399 }
400
401 /**
402  * Determine a proper way to call drush again
403  *
404  * This check if we were called directly or as an argument to some
405  * wrapper command (php and sudo are checked now).
406  *
407  * Calling ./drush.php directly yields the following environment:
408  *
409  * _SERVER["argv"][0] => ./drush.php
410  *
411  * Calling php ./drush.php also yields the following:
412  *
413  * _SERVER["argv"][0] => ./drush.php
414  *
415  * Note that the $_ global is defined only in bash and therefore cannot
416  * be relied upon.
417  *
418  * The DRUSH_COMMAND constant is initialised to the value of this
419  * function when environment.inc is loaded.
420  *
421  * @see DRUSH_COMMAND
422  */
423 function drush_find_drush() {
424   if ($drush = realpath($_SERVER['argv']['0'])) {
425     return Path::canonicalize($drush);
426   }
427   return FALSE;
428 }
429
430 /**
431  * Verify that we are running PHP through the command line interface.
432  *
433  * This function is useful for making sure that code cannot be run via the web server,
434  * such as a function that needs to write files to which the web server should not have
435  * access to.
436  *
437  * @return
438  *   A boolean value that is true when PHP is being run through the command line,
439  *   and false if being run through cgi or mod_php.
440  */
441 function drush_verify_cli() {
442   return (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0));
443 }
444
445 /**
446  * Build a drush command suitable for use for Drush to call itself
447  * e.g. in backend_invoke.
448  */
449 function drush_build_drush_command($drush_path = NULL, $php = NULL, $os = NULL, $remote_command = FALSE, $environment_variables = array()) {
450   $os = _drush_get_os($os);
451   $additional_options = '';
452   $prefix = '';
453   if (!$drush_path) {
454     if (!$remote_command) {
455       $drush_path = DRUSH_COMMAND;
456     }
457     else {
458       $drush_path = 'drush'; // drush_is_windows($os) ? 'drush.bat' : 'drush';
459     }
460   }
461   // If the path to drush points to drush.php, then we will need to
462   // run it via php rather than direct execution.  By default, we
463   // will use 'php' unless something more specific was passed in
464   // via the --php flag.
465   if (substr($drush_path, -4) == ".php") {
466     if (!isset($php)) {
467       $php = drush_get_option('php');
468       if (!isset($php)) {
469         $php = 'php';
470       }
471     }
472     if (isset($php) && ($php != "php")) {
473       $additional_options .= ' --php=' . drush_escapeshellarg($php, $os);
474     }
475     // We will also add in the php options from --php-options
476     $prefix .= drush_escapeshellarg($php, $os);
477     $php_options = implode(' ', drush_get_context_options('php-options'));
478     if (!empty($php_options)) {
479       $prefix .= ' ' . $php_options;
480       $additional_options .= ' --php-options=' . drush_escapeshellarg($php_options, $os);
481     }
482   }
483   else {
484     // Set environment variables to propogate config to redispatched calls.
485     if (drush_has_bash($os)) {
486       if ($php) {
487         $environment_variables['DRUSH_PHP'] = $php;
488       }
489       if ($php_options_alias = drush_get_option('php-options', NULL, 'alias')) {
490         $environment_variables['PHP_OPTIONS'] = $php_options_alias;
491       }
492       $columns = drush_get_context('DRUSH_COLUMNS');
493       if (($columns) && ($columns != 80)) {
494         $environment_variables['COLUMNS'] = $columns;
495       }
496     }
497   }
498
499   // Add environmental variables, if present
500   if (!empty($environment_variables)) {
501     $env = 'env';
502     foreach ($environment_variables as $key=>$value) {
503       $env .= ' ' . drush_escapeshellarg($key, $os) . '=' . drush_escapeshellarg($value, $os);
504     }
505     $prefix = $env . ' ' . $prefix;
506   }
507
508   return trim($prefix . ' ' . drush_escapeshellarg($drush_path, $os) . $additional_options);
509 }
510
511 /**
512  * Check if the operating system is Winodws
513  * running some variant of cygwin -- either
514  * Cygwin or the MSYSGIT shell.  If you care
515  * which is which, test mingw first.
516  */
517 function drush_is_cygwin($os = NULL) {
518   return _drush_test_os($os, array("CYGWIN","CWRSYNC","MINGW"));
519 }
520
521 function drush_is_mingw($os = NULL) {
522   return _drush_test_os($os, array("MINGW"));
523 }
524
525 /**
526  * Return tar executable name specific for the current OS
527  */
528 function drush_get_tar_executable() {
529   return drush_is_windows() ? (drush_is_mingw() ? "tar.exe" : "bsdtar.exe") : "tar";
530 }
531
532 /**
533  * Check if the operating system is OS X.
534  * This will return TRUE for Mac OS X (Darwin).
535  */
536 function drush_is_osx($os = NULL) {
537   return _drush_test_os($os, array("DARWIN"));
538 }
539
540 /**
541  * Checks if the operating system has bash.
542  *
543  * MinGW has bash, but PHP isn't part of MinGW and hence doesn't run in bash.
544  */
545 function drush_has_bash($os = NULL) {
546   return (drush_is_cygwin($os) && !drush_is_mingw($os)) || !drush_is_windows($os);
547 }
548
549 /**
550  * Checks operating system and returns
551  * supported bit bucket folder.
552  */
553 function drush_bit_bucket() {
554   if (drush_has_bash()) {
555     return '/dev/null';
556   }
557   else {
558     return 'nul';
559   }
560 }
561
562 /**
563  * Return the OS we are running under.
564  *
565  * @return string
566  *   Linux
567  *   WIN* (e.g. WINNT)
568  *   CYGWIN
569  *   MINGW* (e.g. MINGW32)
570  */
571 function _drush_get_os($os = NULL) {
572   // The special os "CWRSYNC" can be used to indicate that we are testing
573   // a path that will be passed as an argument to cwRsync, which requires
574   // that the path be converted to /cygdrive/c/path, even on DOS or Powershell.
575   // The special os "RSYNC" can be used to indicate that we want to assume
576   // "CWRSYNC" when cwrsync is installed, or default to the local OS otherwise.
577   if (strtoupper($os) == "RSYNC") {
578     $os = _drush_get_os("LOCAL");
579     // For now we assume that cwrsync is always installed on Windows, and never installed son any other platform.
580     return drush_is_windows($os) ? "CWRSYNC" : $os;
581   }
582   // We allow "LOCAL" to document, in instances where some parameters are being escaped
583   // for use on a remote machine, that one particular parameter will always be used on
584   // the local machine (c.f. drush_backend_invoke).
585   if (isset($os) && ($os != "LOCAL")) {
586     return $os;
587   }
588   if (_drush_test_os(getenv("MSYSTEM"), array("MINGW"))) {
589     return getenv("MSYSTEM");
590   }
591   // QUESTION: Can we differentiate between DOS and POWERSHELL? They appear to have the same environment.
592   // At the moment, it does not seem to matter; they behave the same from PHP.
593   // At this point we will just return PHP_OS.
594   return PHP_OS;
595 }
596
597 function _drush_test_os($os, $os_list_to_check) {
598   $os = _drush_get_os($os);
599   foreach ($os_list_to_check as $test) {
600     if (strtoupper(substr($os, 0, strlen($test))) == strtoupper($test)) {
601       return TRUE;
602     }
603   }
604   return FALSE;
605 }
606
607 /**
608  * Read the drush info file.
609  */
610 function drush_read_drush_info() {
611   $drush_info_file = dirname(__FILE__) . '/../drush.info';
612
613   return parse_ini_file($drush_info_file);
614 }
615
616 /**
617  * Make a determination whether or not the given
618  * host is local or not.
619  *
620  * @param host
621  *   A hostname, 'localhost' or '127.0.0.1'.
622  * @return
623  *   True if the host is local.
624  */
625 function drush_is_local_host($host) {
626   // Check to see if the provided host is "local".
627   // @see hook_drush_sitealias_alter() in drush.api.php.
628   if (
629     ($host == 'localhost') ||
630     ($host == '127.0.0.1')
631   ) {
632     return TRUE;
633   }
634
635   return FALSE;
636 }
637
638 /**
639  * Return the user's home directory.
640  */
641 function drush_server_home() {
642   try {
643     return Path::getHomeDirectory();
644   } catch (Exception $e) {
645     return NULL;
646   }
647 }
648
649 /**
650  * Return the name of the user running drush.
651  */
652 function drush_get_username() {
653   $name = NULL;
654   if (!$name = getenv("username")) { // Windows
655     if (!$name = getenv("USER")) {
656       // If USER not defined, use posix
657       if (function_exists('posix_getpwuid')) {
658         $processUser = posix_getpwuid(posix_geteuid());
659         $name = $processUser['name'];
660       }
661     }
662   }
663   return $name;
664 }
665
666 /**
667  * The path to the global cache directory.
668  *
669  * @param subdir
670  *   Return the specified subdirectory inside the global
671  *   cache directory instead.  The subdirectory is created.
672  */
673 function drush_directory_cache($subdir = '') {
674   $cache_locations = array();
675   if (getenv('CACHE_PREFIX')) {
676     $cache_locations[getenv('CACHE_PREFIX')] = 'cache';
677   }
678   $home = drush_server_home();
679   if ($home) {
680     $cache_locations[$home] = '.drush/cache';
681   }
682   $cache_locations[drush_find_tmp()] = 'drush-' . drush_get_username() . '/cache';
683   foreach ($cache_locations as $base => $dir) {
684     if (!empty($base) && is_writable($base)) {
685       $cache_dir = $base . '/' . $dir;
686       if (!empty($subdir)) {
687         $cache_dir .= '/' . $subdir;
688       }
689       if (drush_mkdir($cache_dir)) {
690         return $cache_dir;
691       }
692       else {
693         // If the base directory is writable, but the cache directory
694         // is not, then we will get an error. The error will be displayed,
695         // but we will still call drush_clear_error so that we can go
696         // on and try the next location to see if we can find a cache
697         // directory somewhere.
698         drush_clear_error();
699       }
700     }
701   }
702   return drush_set_error('DRUSH_NO_WRITABLE_CACHE', dt('Drush must have a writable cache directory; please insure that one of the following locations is writable: @locations',
703     array('@locations' => implode(',', array_keys($cache_locations)))));
704 }
705
706 /**
707  * Get complete information for all available extensions (modules and themes).
708  *
709  * @return
710  *   An array containing info for all available extensions. In D8, these are Extension objects.
711  */
712 function drush_get_extensions($include_hidden = TRUE) {
713   drush_include_engine('drupal', 'environment');
714   $extensions = array_merge(drush_get_modules($include_hidden), drush_get_themes($include_hidden));
715   foreach ($extensions as $name => $extension) {
716     if (isset($extension->info['name'])) {
717       $extensions[$name]->label = $extension->info['name'].' ('.$name.')';
718     }
719     else {
720       drush_log(dt("Extension !name provides no human-readable name in .info file.", array('!name' => $name), LogLevel::DEBUG));
721       $extensions[$name]->label = $name.' ('.$name.')';
722     }
723     if (empty($extension->info['package'])) {
724       $extensions[$name]->info['package'] = dt('Other');
725     }
726   }
727   return $extensions;
728 }
729
730 /**
731  * Gets the extension name.
732  *
733  * @param $info
734  *   The extension info.
735  * @return string
736  *   The extension name.
737  */
738 function drush_extension_get_name($info) {
739   drush_include_engine('drupal', 'environment');
740   return _drush_extension_get_name($info);
741 }
742
743 /**
744  * Gets the extension type.
745  *
746  * @param $info
747  *   The extension info.
748  * @return string
749  *   The extension type.
750  */
751 function drush_extension_get_type($info) {
752   drush_include_engine('drupal', 'environment');
753   return _drush_extension_get_type($info);
754 }
755
756 /**
757  * Gets the extension path.
758  *
759  * @param $info
760  *   The extension info.
761  * @return string
762  *   The extension path.
763  */
764 function drush_extension_get_path($info) {
765   drush_include_engine('drupal', 'environment');
766   return _drush_extension_get_path($info);
767 }
768
769 /**
770  * Test compatibility of a extension with version of drupal core and php.
771  *
772  * @param $file Extension object as returned by system_rebuild_module_data().
773  * @return FALSE if the extension is compatible.
774  */
775 function drush_extension_check_incompatibility($file) {
776   if (!isset($file->info['core']) || $file->info['core'] != drush_get_drupal_core_compatibility()) {
777     return 'Drupal';
778   }
779   if (version_compare(phpversion(), $file->info['php']) < 0) {
780     return 'PHP';
781   }
782   return FALSE;
783 }
784
785 /**
786  *
787  */
788 function drush_drupal_required_modules($modules) {
789   drush_include_engine('drupal', 'environment');
790   return _drush_drupal_required_modules($modules);
791 }
792
793 /**
794  * Return the default theme.
795  *
796  * @return
797  *  Machine name of the default theme.
798  */
799 function drush_theme_get_default() {
800   drush_include_engine('drupal', 'environment');
801   return _drush_theme_default();
802 }
803
804 /**
805  * Return the administration theme.
806  *
807  * @return
808  *  Machine name of the administration theme.
809  */
810 function drush_theme_get_admin() {
811   drush_include_engine('drupal', 'environment');
812   return _drush_theme_admin();
813 }
814
815 /**
816  * Return the path to public files directory.
817  */
818 function drush_file_get_public() {
819   drush_include_engine('drupal', 'environment');
820   return _drush_file_public_path();
821 }
822
823 /**
824  * Return the path to private files directory.
825  */
826 function drush_file_get_private() {
827   drush_include_engine('drupal', 'environment');
828   return _drush_file_private_path();
829 }
830
831 /**
832  * Returns the sitewide Drupal directory for extensions.
833  */
834 function drush_drupal_sitewide_directory($major_version = NULL) {
835   if (!$major_version) {
836     $major_version = drush_drupal_major_version();
837   }
838   return ($major_version < 8) ? 'sites/all' : '';
839 }
840
841 /**
842  * Helper function to get core compatibility constant.
843  *
844  * @return string
845  *   The Drupal core compatibility constant.
846  */
847 function drush_get_drupal_core_compatibility() {
848   if (defined('DRUPAL_CORE_COMPATIBILITY')) {
849     return DRUPAL_CORE_COMPATIBILITY;
850   }
851   elseif (defined('\Drupal::CORE_COMPATIBILITY')) {
852     return \Drupal::CORE_COMPATIBILITY;
853   }
854 }
855
856 /**
857  * Set Env. Variables for given site-alias.
858  */
859 function drush_set_environment_vars(array $site_record) {
860   if (!empty($site_record)) {
861     $os = drush_os($site_record);
862     if (isset($site_record['#env-vars'])) {
863       foreach ($site_record['#env-vars'] as $var => $value) {
864         $env_var = drush_escapeshellarg($var, $os, TRUE) . '=' . drush_escapeshellarg($value, $os, TRUE);
865         putenv($env_var);
866       }
867     }
868   }
869 }