e7b6d2ea97a43d9e30765167f593203245d54595
[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 Drush\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         $this->aliasManager = (new SiteAliasManager())->addSearchLocations($paths);
259         $this->aliasManager->setReferenceData($config->export());
260         $siteLocator = new PreflightSiteLocator($this->aliasManager);
261         $selfAliasRecord = $siteLocator->findSite($this->preflightArgs, $this->environment, $root);
262         $this->aliasManager->setSelf($selfAliasRecord);
263         $this->configLocator->addAliasConfig($selfAliasRecord->exportConfig());
264
265         // Process the selected alias. This might change the selected site,
266         // so we will add new site-wide config location for the new root.
267         $root = $this->setSelectedSite($selfAliasRecord->localRoot());
268
269         // Now that we have our final Drupal root, check to see if there is
270         // a site-local Drush. If there is, we will redispatch to it.
271         // NOTE: termination handlers have not been set yet, so it is okay
272         // to exit early without taking special action.
273         $status = RedispatchToSiteLocal::redispatchIfSiteLocalDrush($argv, $root, $this->environment->vendorPath(), $this->logger())    ;
274         if ($status !== false) {
275             return $status;
276         }
277
278         // If we did not redispatch, then add the site-wide config for the
279         // new root (if the root did in fact change) and continue.
280         $this->configLocator->addSitewideConfig($root);
281
282         // Remember the paths to all the files we loaded, so that we can
283         // report on it from Drush status or wherever else it may be needed.
284         $configFilePaths = $this->configLocator->configFilePaths();
285         $config->set('runtime.config.paths', $configFilePaths);
286         $this->logger()->log(dt('Config paths: ' . implode(',', $configFilePaths)));
287         $this->logger()->log(dt('Alias paths: ' . implode(',', $paths)));
288
289         // We need to check the php minimum version again, in case anyone
290         // has set it to something higher in one of the config files we loaded.
291         $this->verify->confirmPhpVersion($config->get('drush.php.minimum-version'));
292
293         return false;
294     }
295
296     /**
297      * Find the site the user selected based on --root or cwd. If neither of
298      * those result in a site, then we will fall back to the vendor path.
299      */
300     protected function findSelectedSite()
301     {
302         // TODO: If we want to support ONLY site-local Drush (which is
303         // DIFFERENT than --local), then skip the call to `$preflightArgs->selectedSite`
304         // and just assign `false` to $selectedRoot.
305
306         // Try two approaches.
307         $selectedRoot = $this->preflightArgs->selectedSite($this->environment->cwd());
308         $fallBackPath = $this->preflightArgs->selectedSite(DRUSH_COMMAND);
309         return $this->setSelectedSite($selectedRoot, $fallBackPath);
310     }
311
312     /**
313      * Use the DrupalFinder to locate the Drupal Root + Composer Root at
314      * the selected root, or, if nothing is found there, at a fallback path.
315      *
316      * @param string $selectedRoot The location to being searching for a site
317      * @param string|bool $fallbackPath The secondary location to search (usualy the vendor director)
318      */
319     protected function setSelectedSite($selectedRoot, $fallbackPath = false)
320     {
321         if ($selectedRoot || $fallbackPath) {
322             $foundRoot = $this->drupalFinder->locateRoot($selectedRoot);
323             if (!$foundRoot && $fallbackPath) {
324                 $this->drupalFinder->locateRoot($fallbackPath);
325             }
326             return $this->drupalFinder()->getDrupalRoot();
327         }
328     }
329
330     /**
331      * Return the Drupal Finder
332      *
333      * @return DrupalFinder
334      */
335     public function drupalFinder()
336     {
337         return $this->drupalFinder;
338     }
339
340     /**
341      * Return the alias manager
342      *
343      * @return SiteAliasManager
344      */
345     public function aliasManager()
346     {
347         return $this->aliasManager;
348     }
349
350     /**
351      * Return the environment
352      *
353      * @return Environment
354      */
355     public function environment()
356     {
357         return $this->environment;
358     }
359 }