Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / drush / drush / src / Config / Environment.php
1 <?php
2 namespace Drush\Config;
3
4 use Composer\Autoload\ClassLoader;
5
6 use Drush\Drush;
7 use Drush\Utils\FsUtils;
8 use Webmozart\PathUtil\Path;
9
10 /**
11  * Store information about the environment
12  */
13 class Environment
14 {
15     protected $homeDir;
16     protected $originalCwd;
17     protected $etcPrefix;
18     protected $sharePrefix;
19     protected $drushBasePath;
20     protected $vendorDir;
21
22     protected $docPrefix;
23     protected $configFileVariant;
24
25     protected $loader;
26     protected $siteLoader;
27
28     /**
29      * Environment constructor
30      * @param string $homeDir User home directory.
31      * @param string $cwd The current working directory at the time Drush was called.
32      * @param string $autoloadFile Path to the autoload.php file.
33      */
34     public function __construct($homeDir, $cwd, $autoloadFile)
35     {
36         $this->homeDir = $homeDir;
37         $this->originalCwd = Path::canonicalize($cwd);
38         $this->etcPrefix = '';
39         $this->sharePrefix = '';
40         $this->drushBasePath = dirname(dirname(__DIR__));
41         $this->vendorDir = FsUtils::realpath(dirname($autoloadFile));
42     }
43
44     /**
45      * Load the autoloader for the selected Drupal site
46      *
47      * @param string $root
48      * @return ClassLoader
49      */
50     public function loadSiteAutoloader($root)
51     {
52         $autloadFilePath = "$root/autoload.php";
53         if (!file_exists($autloadFilePath)) {
54             return $this->loader;
55         }
56
57         if ($this->siteLoader) {
58             return $this->siteLoader;
59         }
60
61         $this->siteLoader = require $autloadFilePath;
62         if ($this->siteLoader === false) {
63             // Nothing more to do. See https://github.com/drush-ops/drush/issues/3741.
64             return $this->loader;
65         }
66         if ($this->siteLoader === true) {
67             // The autoloader was already required. Assume that Drush and Drupal share an autoloader per
68             // "Point autoload.php to the proper vendor directory" - https://www.drupal.org/node/2404989
69             $this->siteLoader = $this->loader;
70         }
71
72         // Ensure that the site's autoloader has highest priority. Usually,
73         // the first classloader registered gets the first shot at loading classes.
74         // We want Drupal's classloader to be used first when a class is loaded,
75         // and have Drush's classloader only be called as a fallback measure.
76         $this->siteLoader->unregister();
77         $this->siteLoader->register(true);
78
79         return $this->siteLoader;
80     }
81
82     /**
83      * Return the name of the user running drush.
84      *
85      * @return string
86      */
87     protected function getUsername()
88     {
89         $name = null;
90         if (!$name = getenv("username")) { // Windows
91             if (!$name = getenv("USER")) {
92                 // If USER not defined, use posix
93                 if (function_exists('posix_getpwuid')) {
94                     $processUser = posix_getpwuid(posix_geteuid());
95                     $name = $processUser['name'];
96                 }
97             }
98         }
99         return $name;
100     }
101
102     protected function getTmp()
103     {
104         $directories = [];
105
106         // Get user specific and operating system temp folders from system environment variables.
107         // See http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds_shelloverview.mspx?mfr=true
108         $tempdir = getenv('TEMP');
109         if (!empty($tempdir)) {
110             $directories[] = $tempdir;
111         }
112         $tmpdir = getenv('TMP');
113         if (!empty($tmpdir)) {
114             $directories[] = $tmpdir;
115         }
116         // Operating system specific dirs.
117         if (self::isWindows()) {
118             $windir = getenv('WINDIR');
119             if (isset($windir)) {
120                 // WINDIR itself is not writable, but it always contains a /Temp dir,
121                 // which is the system-wide temporary directory on older versions. Newer
122                 // versions only allow system processes to use it.
123                 $directories[] = Path::join($windir, 'Temp');
124             }
125         } else {
126             $directories[] = Path::canonicalize('/tmp');
127         }
128         $directories[] = Path::canonicalize(sys_get_temp_dir());
129
130         foreach ($directories as $directory) {
131             if (is_dir($directory) && is_writable($directory)) {
132                 $temporary_directory = $directory;
133                 break;
134             }
135         }
136
137         if (empty($temporary_directory)) {
138             // If no directory has been found, create one in cwd.
139             $temporary_directory = Path::join(Drush::config()->cwd(), 'tmp');
140             drush_mkdir($temporary_directory, true);
141             if (!is_dir($temporary_directory)) {
142                 throw new \Exception(dt("Unable to create a temporary directory."));
143             }
144             // Function not available yet - this is not likely to get reached anyway.
145             // drush_register_file_for_deletion($temporary_directory);
146         }
147         return $temporary_directory;
148     }
149
150     /**
151      * Convert the environment object into an exported configuration
152      * array.
153      *
154      * @see PreflightArgs::applyToConfig(), which also exports information to config.
155      *
156      * @return array Nested associative array that is overlayed on configuration.
157      */
158     public function exportConfigData()
159     {
160         return [
161             // Information about the environment presented to Drush
162             'env' => [
163                 'cwd' => $this->cwd(),
164                 'home' => $this->homeDir(),
165                 'user' => $this->getUsername(),
166                 'is-windows' => $this->isWindows(),
167                 'tmp' => $this->getTmp(),
168             ],
169             // These values are available as global options, and
170             // will be passed in to the FormatterOptions et. al.
171             'options' => [
172                 'width' => $this->calculateColumns(),
173             ],
174             // Information about the directories where Drush found assets, etc.
175             'drush' => [
176                 'base-dir' => $this->drushBasePath,
177                 'vendor-dir' => $this->vendorPath(),
178                 'docs-dir' => $this->docsPath(),
179                 'user-dir' => $this->userConfigPath(),
180                 'system-dir' => $this->systemConfigPath(),
181                 'system-command-dir' => $this->systemCommandFilePath(),
182             ],
183             'runtime' => [
184                 'site-file-previous' => $this->getSiteSetAliasFilePath('drush-drupal-prev-site-'),
185                 'site-file-current' => $this->getSiteSetAliasFilePath(),
186             ],
187         ];
188     }
189
190     /**
191      * The base directory of the Drush application itself
192      * (where composer.json et.al. are found)
193      *
194      * @return string
195      */
196     public function drushBasePath()
197     {
198         return $this->drushBasePath;
199     }
200
201     /**
202      * Get the site:set alias from the current site:set file path.
203      *
204      * @return bool|string
205      */
206     public function getSiteSetAliasName()
207     {
208         $site_filename = $this->getSiteSetAliasFilePath();
209         if (file_exists($site_filename)) {
210             $site = file_get_contents($site_filename);
211             if ($site) {
212                 return $site;
213             }
214         }
215         return false;
216     }
217
218     /**
219      * User's home directory
220      *
221      * @return string
222      */
223     public function homeDir()
224     {
225         return $this->homeDir;
226     }
227
228     /**
229      * The user's Drush configuration directory, ~/.drush
230      *
231      * @return string
232      */
233     public function userConfigPath()
234     {
235         return $this->homeDir() . '/.drush';
236     }
237
238     public function setConfigFileVariant($variant)
239     {
240         $this->configFileVariant = $variant;
241     }
242
243     /**
244      * Get the config file variant -- defined to be
245      * the Drush major version number. This is for
246      * loading drush.yml and drush9.yml, etc.
247      */
248     public function getConfigFileVariant()
249     {
250         return $this->configFileVariant;
251     }
252
253     /**
254      * The original working directory
255      *
256      * @return string
257      */
258     public function cwd()
259     {
260         return $this->originalCwd;
261     }
262
263     /**
264      * Return the path to Drush's vendor directory
265      *
266      * @return string
267      */
268     public function vendorPath()
269     {
270         return $this->vendorDir;
271     }
272
273     /**
274      * The class loader returned when the autoload.php file is included.
275      *
276      * @return \Composer\Autoload\ClassLoader
277      */
278     public function loader()
279     {
280         return $this->loader;
281     }
282
283     /**
284      * Set the class loader from the autload.php file, if available.
285      *
286      * @param \Composer\Autoload\ClassLoader $loader
287      */
288     public function setLoader(ClassLoader $loader)
289     {
290         $this->loader = $loader;
291     }
292
293     /**
294      * Alter our default locations based on the value of environment variables
295      *
296      * @return $this
297      */
298     public function applyEnvironment()
299     {
300         // Copy ETC_PREFIX and SHARE_PREFIX from environment variables if available.
301         // This alters where we check for server-wide config and alias files.
302         // Used by unit test suite to provide a clean environment.
303         $this->setEtcPrefix(getenv('ETC_PREFIX'));
304         $this->setSharePrefix(getenv('SHARE_PREFIX'));
305
306         return $this;
307     }
308
309     /**
310      * Set the directory prefix to locate the directory that Drush will
311      * use as /etc (e.g. during the functional tests)
312      *
313      * @param string $etcPrefix
314      * @return $this
315      */
316     public function setEtcPrefix($etcPrefix)
317     {
318         if (isset($etcPrefix)) {
319             $this->etcPrefix = $etcPrefix;
320         }
321         return $this;
322     }
323
324     /**
325      * Set the directory prefix to locate the directory that Drush will
326      * use as /user/share (e.g. during the functional tests)
327      * @param string $sharePrefix
328      * @return $this
329      */
330     public function setSharePrefix($sharePrefix)
331     {
332         if (isset($sharePrefix)) {
333             $this->sharePrefix = $sharePrefix;
334             $this->docPrefix = null;
335         }
336         return $this;
337     }
338
339     /**
340      * Return the directory where Drush's documentation is stored. Usually
341      * this is within the Drush application, but some Drush RPM distributions
342      * & c. for Linux platforms slice-and-dice the contents and put the docs
343      * elsewhere.
344      *
345      * @return string
346      */
347     public function docsPath()
348     {
349         if (!$this->docPrefix) {
350             $this->docPrefix = $this->findDocsPath($this->drushBasePath);
351         }
352         return $this->docPrefix;
353     }
354
355     /**
356      * Locate the Drush documentation. This is recalculated whenever the
357      * share prefix is changed.
358      *
359      * @param string $drushBasePath
360      * @return string
361      */
362     protected function findDocsPath($drushBasePath)
363     {
364         $candidates = [
365             "$drushBasePath/README.md",
366             static::systemPathPrefix($this->sharePrefix, '/usr') . '/share/docs/drush/README.md',
367         ];
368         return $this->findFromCandidates($candidates);
369     }
370
371     /**
372      * Check a list of directories and return the first one that exists.
373      *
374      * @param array $candidates
375      * @return string|boolean
376      */
377     protected function findFromCandidates($candidates)
378     {
379         foreach ($candidates as $candidate) {
380             if (file_exists($candidate)) {
381                 return dirname($candidate);
382             }
383         }
384         return false;
385     }
386
387     /**
388      * Return the appropriate system path prefix, unless an override is provided.
389      * @param string $override
390      * @param string $defaultPrefix
391      * @return string
392      */
393     protected static function systemPathPrefix($override = '', $defaultPrefix = '')
394     {
395         if ($override) {
396             return $override;
397         }
398         return static::isWindows() ? getenv('ALLUSERSPROFILE') . '/Drush' : $defaultPrefix;
399     }
400
401     /**
402      * Return the system configuration path (default: /etc/drush)
403      *
404      * @return string
405      */
406     public function systemConfigPath()
407     {
408         return static::systemPathPrefix($this->etcPrefix, '') . '/etc/drush';
409     }
410
411     /**
412      * Return the system shared commandfile path (default: /usr/share/drush/commands)
413      *
414      * @return string
415      */
416     public function systemCommandFilePath()
417     {
418         return static::systemPathPrefix($this->sharePrefix, '/usr') . '/share/drush/commands';
419     }
420
421     /**
422      * Determine whether current OS is a Windows variant.
423      *
424      * @return boolean
425      */
426     public static function isWindows($os = null)
427     {
428         return strtoupper(substr($os ?: PHP_OS, 0, 3)) === 'WIN';
429     }
430
431     /**
432      * Verify that we are running PHP through the command line interface.
433      *
434      * @return boolean
435      *   A boolean value that is true when PHP is being run through the command line,
436      *   and false if being run through cgi or mod_php.
437      */
438     public function verifyCLI()
439     {
440         return (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0));
441     }
442
443     /**
444      * Calculate the terminal width used for wrapping table output.
445      * Normally this is exported using tput in the drush script.
446      * If this is not present we do an additional check using stty here.
447      * On Windows in CMD and PowerShell is this exported using mode con.
448      *
449      * @return integer
450      */
451     public function calculateColumns()
452     {
453         if ($columns = getenv('COLUMNS')) {
454             return $columns;
455         }
456
457         // Trying to export the columns using stty.
458         exec('stty size 2>&1', $columns_output, $columns_status);
459         if (!$columns_status) {
460             $columns = preg_replace('/\d+\s(\d+)/', '$1', $columns_output[0], -1, $columns_count);
461         }
462
463         // If stty fails and Drush us running on Windows are we trying with mode con.
464         if (($columns_status || !$columns_count) && static::isWindows()) {
465             $columns_output = [];
466             exec('mode con', $columns_output, $columns_status);
467             if (!$columns_status && is_array($columns_output)) {
468                 $columns = (int)preg_replace('/\D/', '', $columns_output[4], -1, $columns_count);
469             }
470             // TODO: else { 'Drush could not detect the console window width. Set a Windows Environment Variable of COLUMNS to the desired width.'
471         }
472
473         // Failling back to default columns value
474         if (empty($columns)) {
475             $columns = 80;
476         }
477
478         // TODO: should we deal with reserve-margin here, or adjust it later?
479         return $columns;
480     }
481
482     /**
483      * Returns the filename for the file that stores the DRUPAL_SITE variable.
484      *
485      * @param string $filename_prefix
486      *   An arbitrary string to prefix the filename with.
487      *
488      * @return string|false
489      *   Returns the full path to temp file if possible, or FALSE if not.
490      */
491     protected function getSiteSetAliasFilePath($filename_prefix = 'drush-drupal-site-')
492     {
493         $shell_pid = getenv('DRUSH_SHELL_PID');
494         if (!$shell_pid && function_exists('posix_getppid')) {
495             $shell_pid = posix_getppid();
496         }
497         if (!$shell_pid) {
498             return false;
499         }
500
501         // The env variables below must match the variables in example.prompt.sh
502         $tmp = getenv('TMPDIR') ? getenv('TMPDIR') : '/tmp';
503         $username = $this->getUsername();
504
505         return "{$tmp}/drush-env-{$username}/{$filename_prefix}" . $shell_pid;
506     }
507 }