513a884f9684c0fd1fb2720cb19b775d97717325
[yaffs-website] / vendor / drush / drush / src / Preflight / Preflight.php
1 <?php
2 namespace Drush\Preflight;
3
4 use Drush\Config\Environment;
5 use Drush\Config\ConfigLocator;
6 use Drush\Config\EnvironmentConfigLoader;
7 use Consolidation\SiteAlias\SiteAliasManager;
8 use DrupalFinder\DrupalFinder;
9
10 /**
11  * The Drush preflight determines what needs to be done for this request.
12  * The preflight happens after Drush has loaded its autoload file, but
13  * prior to loading Drupal's autoload file and setting up the DI container.
14  *
15  * - Pre-parse commandline arguments
16  * - Read configuration .yml files
17  * - Determine the site to use
18  */
19 class Preflight
20 {
21     /**
22      * @var Environment $environment
23      */
24     protected $environment;
25
26     /**
27      * @var PreflightVerify
28      */
29     protected $verify;
30
31     /**
32      * @var ConfigLocator
33      */
34     protected $configLocator;
35
36     /**
37      * @var DrupalFinder
38      */
39     protected $drupalFinder;
40
41     /**
42      * @var PreflightArgs
43      */
44     protected $preflightArgs;
45
46     /**
47      * @var SiteAliasManager
48      */
49     protected $aliasManager;
50
51     /**
52      * @var PreflightLog $logger An early logger, just for Preflight.
53      */
54     protected $logger;
55
56     /**
57      * Preflight constructor
58      */
59     public function __construct(Environment $environment, $verify = null, $configLocator = null)
60     {
61         $this->environment = $environment;
62         $this->verify = $verify ?: new PreflightVerify();
63         $this->configLocator = $configLocator ?: new ConfigLocator('DRUSH_', $environment->getConfigFileVariant());
64         $this->drupalFinder = new DrupalFinder();
65         $this->logger = new PreflightLog();
66     }
67
68     /**
69      * @return PreflightLog
70      */
71     public function logger()
72     {
73         return $this->logger;
74     }
75
76     /**
77      * @param PreflightLog $logger
78      */
79     public function setLogger(PreflightLog $logger)
80     {
81         $this->logger = $logger;
82     }
83
84     /**
85      * Perform preliminary initialization. This mostly involves setting up
86      * legacy systems.
87      */
88     public function init()
89     {
90         // Define legacy constants, and include legacy files that Drush still needs
91         LegacyPreflight::includeCode($this->environment->drushBasePath());
92         LegacyPreflight::defineConstants($this->environment, $this->preflightArgs->applicationPath());
93         LegacyPreflight::setContexts($this->environment);
94     }
95
96     /**
97      * Remapping table for arguments. Anything found in a key
98      * here will be converted to the corresponding value entry.
99      *
100      * For example:
101      *    --ssh-options='-i mysite_dsa'
102      * will become:
103      *    -Dssh.options='-i mysite_dsa'
104      *
105      * TODO: We could consider loading this from a file or some other
106      * source. However, this table is needed very early -- even earlier
107      * than config is loaded (since this is needed for preflighting the
108      * arguments, which can select config files to load). Hardcoding
109      * is probably best; we might want to move to another class, perhaps.
110      * We also need this prior to Dependency Injection, though.
111      *
112      * Eventually, we might want to expose this table to some form of
113      * 'help' output, so folks can see the available conversions.
114      */
115     protected function remapOptions()
116     {
117         return [
118             '--ssh-options' => '-Dssh.options',
119             '--php' => '-Druntime.php.path',
120             '--php-options' => '-Druntime.php.options',
121             '--php-notices' => '-Druntime.php.notices',
122             '--halt-on-error' => '-Druntime.php.halt-on-error',
123             '--output_charset' => '-Dio.output.charset',
124             '--output-charset' => '-Dio.output.charset',
125             '--db-su' => '-Dsql.db-su',
126             '--notify' => '-Dnotify.duration',
127             '--xh-link' => '-Dxh.link',
128         ];
129     }
130
131     /**
132      * Symfony Console dislikes certain command aliases, because
133      * they are too similar to other Drush commands that contain
134      * the same characters.  To avoid the "I don't know which
135      * command you mean"-type errors, we will replace problematic
136      * aliases with their longhand equivalents.
137      *
138      * This should be fixed in Symfony Console.
139      */
140     protected function remapCommandAliases()
141     {
142         return [
143             'si' => 'site:install',
144             'en' => 'pm:enable',
145             // php was an alias for core-cli which got renamed to php-cli. See https://github.com/drush-ops/drush/issues/3091.
146             'php' => 'php:cli',
147         ];
148     }
149
150     /**
151      * Preprocess the args, removing any @sitealias that may be present.
152      * Arguments and options not used during preflight will be processed
153      * with an ArgvInput.
154      */
155     public function preflightArgs($argv)
156     {
157         $argProcessor = new ArgsPreprocessor();
158         $remapper = new ArgsRemapper($this->remapOptions(), $this->remapCommandAliases());
159         $preflightArgs = new PreflightArgs();
160         $preflightArgs->setHomeDir($this->environment()->homeDir());
161         $argProcessor->setArgsRemapper($remapper);
162
163         $argProcessor->parse($argv, $preflightArgs);
164
165         return $preflightArgs;
166     }
167
168     /**
169      * Create the initial config locator object, and inject any needed
170      * settings, paths and so on into it.
171      */
172     public function prepareConfig(Environment $environment)
173     {
174         // Make our environment settings available as configuration items
175         $this->configLocator->addEnvironment($environment);
176         $this->configLocator->setLocal($this->preflightArgs->isLocal());
177         $this->configLocator->addUserConfig($this->preflightArgs->configPaths(), $environment->systemConfigPath(), $environment->userConfigPath());
178         $this->configLocator->addDrushConfig($environment->drushBasePath());
179     }
180
181     /**
182      * Start code coverage collection
183      */
184     public function startCoverage()
185     {
186         if ($coverage_file = $this->preflightArgs->coverageFile()) {
187             // TODO: modernize code coverage handling
188             drush_set_context('DRUSH_CODE_COVERAGE', $coverage_file);
189             xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
190             register_shutdown_function('drush_coverage_shutdown');
191         }
192     }
193
194     public function createInput()
195     {
196         return $this->preflightArgs->createInput();
197     }
198
199     public function getCommandFilePaths()
200     {
201         // Find all of the available commandfiles, save for those that are
202         // provided by modules in the selected site; those will be added
203         // during bootstrap.
204         return $this->configLocator->getCommandFilePaths($this->preflightArgs->commandPaths(), $this->drupalFinder()->getDrupalRoot());
205     }
206
207     public function loadSiteAutoloader()
208     {
209         return $this->environment()->loadSiteAutoloader($this->drupalFinder()->getDrupalRoot());
210     }
211
212     public function config()
213     {
214         return $this->configLocator->config();
215     }
216
217     /**
218      * @param $argv
219      * @return bool
220      *   True if the request was successfully redispatched remotely. False if the request should proceed.
221      */
222     public function preflight($argv)
223     {
224         // Fail fast if there is anything in our environment that does not check out
225         $this->verify->verify($this->environment);
226
227         // Get the preflight args and begin collecting configuration files.
228         $this->preflightArgs = $this->preflightArgs($argv);
229         $this->prepareConfig($this->environment);
230
231         // Now that we know the value, set debug flag.
232         $this->logger()->setDebug($this->preflightArgs->get(PreflightArgs::DEBUG));
233
234         // Do legacy initialization (load static includes, define old constants, etc.)
235         $this->init();
236
237         // Start code coverage
238         $this->startCoverage();
239
240         // Get the config files provided by prepareConfig()
241         $config = $this->config();
242
243         // Copy items from the preflight args into configuration.
244         // This will also load certain config values into the preflight args.
245         $this->preflightArgs->applyToConfig($config);
246
247         // Determine the local site targeted, if any.
248         // Extend configuration and alias files to include files in
249         // target site.
250         $root = $this->findSelectedSite();
251         $this->configLocator->addSitewideConfig($root);
252         $this->configLocator->setComposerRoot($this->drupalFinder()->getComposerRoot());
253
254         // Look up the locations where alias files may be found.
255         $paths = $this->configLocator->getSiteAliasPaths($this->preflightArgs->aliasPaths(), $this->environment);
256
257         // Configure alias manager.
258         $aliasFileLoader = new \Drush\SiteAlias\SiteAliasFileLoader();
259         $this->aliasManager = (new SiteAliasManager($aliasFileLoader))->addSearchLocations($paths);
260         $this->aliasManager->setReferenceData($config->export());
261
262         // Find the local site
263         $siteLocator = new PreflightSiteLocator($this->aliasManager);
264         $selfAliasRecord = $siteLocator->findSite($this->preflightArgs, $this->environment, $root);
265
266         // If we did not find a local site, then we are destined to fail
267         // UNLESS RedispatchToSiteLocal::redispatchIfSiteLocalDrush takes over.
268         // Before we try to redispatch to the site-local Drush, though, we must
269         // initiaze the alias manager & c. based on any alias record we did find.
270         if ($selfAliasRecord) {
271             $this->aliasManager->setSelf($selfAliasRecord);
272             $this->configLocator->addAliasConfig($selfAliasRecord->exportConfig());
273
274             // Process the selected alias. This might change the selected site,
275             // so we will add new site-wide config location for the new root.
276             $root = $this->setSelectedSite($selfAliasRecord->localRoot());
277         }
278
279         // Now that we have our final Drupal root, check to see if there is
280         // a site-local Drush. If there is, we will redispatch to it.
281         // NOTE: termination handlers have not been set yet, so it is okay
282         // to exit early without taking special action.
283         $status = RedispatchToSiteLocal::redispatchIfSiteLocalDrush($argv, $root, $this->environment->vendorPath(), $this->logger())    ;
284         if ($status !== false) {
285             return $status;
286         }
287
288         // If the site locator couldn't find a local site, and we did not
289         // redispatch to a site-local Drush, then we cannot continue.
290         // This can happen when using Drush 9 to call a site-local Drush 8
291         // using an alias record that is only defined in a Drush 8 format.
292         if (!$selfAliasRecord) {
293             // Note that PreflightSiteLocator::findSite only returns 'false'
294             // when preflightArgs->alias() returns an alias name. In all other
295             // instances we will get an alias record, even if it is only a
296             // placeholder 'self' with the root holding the cwd.
297             $aliasName = $this->preflightArgs->alias();
298             throw new \Exception("The alias $aliasName could not be found.");
299         }
300
301         // If we did not redispatch, then add the site-wide config for the
302         // new root (if the root did in fact change) and continue.
303         $this->configLocator->addSitewideConfig($root);
304
305         // Remember the paths to all the files we loaded, so that we can
306         // report on it from Drush status or wherever else it may be needed.
307         $configFilePaths = $this->configLocator->configFilePaths();
308         $config->set('runtime.config.paths', $configFilePaths);
309         $this->logger()->log(dt('Config paths: ' . implode(',', $configFilePaths)));
310         $this->logger()->log(dt('Alias paths: ' . implode(',', $paths)));
311
312         // We need to check the php minimum version again, in case anyone
313         // has set it to something higher in one of the config files we loaded.
314         $this->verify->confirmPhpVersion($config->get('drush.php.minimum-version'));
315
316         return false;
317     }
318
319     /**
320      * Find the site the user selected based on --root or cwd. If neither of
321      * those result in a site, then we will fall back to the vendor path.
322      */
323     protected function findSelectedSite()
324     {
325         // TODO: If we want to support ONLY site-local Drush (which is
326         // DIFFERENT than --local), then skip the call to `$preflightArgs->selectedSite`
327         // and just assign `false` to $selectedRoot.
328
329         // Try two approaches.
330         $selectedRoot = $this->preflightArgs->selectedSite($this->environment->cwd());
331         $fallBackPath = $this->preflightArgs->selectedSite(DRUSH_COMMAND);
332         return $this->setSelectedSite($selectedRoot, $fallBackPath);
333     }
334
335     /**
336      * Use the DrupalFinder to locate the Drupal Root + Composer Root at
337      * the selected root, or, if nothing is found there, at a fallback path.
338      *
339      * @param string $selectedRoot The location to being searching for a site
340      * @param string|bool $fallbackPath The secondary location to search (usualy the vendor director)
341      */
342     protected function setSelectedSite($selectedRoot, $fallbackPath = false)
343     {
344         if ($selectedRoot || $fallbackPath) {
345             $foundRoot = $this->drupalFinder->locateRoot($selectedRoot);
346             if (!$foundRoot && $fallbackPath) {
347                 $this->drupalFinder->locateRoot($fallbackPath);
348             }
349             return $this->drupalFinder()->getDrupalRoot();
350         }
351     }
352
353     /**
354      * Return the Drupal Finder
355      *
356      * @return DrupalFinder
357      */
358     public function drupalFinder()
359     {
360         return $this->drupalFinder;
361     }
362
363     /**
364      * Return the alias manager
365      *
366      * @return SiteAliasManager
367      */
368     public function aliasManager()
369     {
370         return $this->aliasManager;
371     }
372
373     /**
374      * Return the environment
375      *
376      * @return Environment
377      */
378     public function environment()
379     {
380         return $this->environment;
381     }
382 }