2 namespace Drush\Runtime;
4 use Consolidation\AnnotatedCommand\CommandData;
5 use Consolidation\AnnotatedCommand\Hooks\ValidatorInterface;
7 use Drush\Preflight\Preflight;
10 * Control the Drush runtime environment
13 * - Symfony application run
26 * @param Preflight $preflight the prefligth object
28 public function __construct(Preflight $preflight)
30 $this->preflight = $preflight;
34 * Run the application, catching any errors that may be thrown.
35 * Typically, this will happen only for code that fails fast during
36 * preflight. Later code should catch and handle its own exceptions.
38 public function run($argv)
41 $status = $this->doRun($argv);
42 } catch (\Exception $e) {
43 $status = $e->getCode();
44 $message = $e->getMessage();
45 // Uncaught exceptions could happen early, before our logger
46 // and other classes are initialized. Print them and exit.
47 $this->preflight->logger()->setDebug(true)->log($message);
55 protected function doRun($argv)
57 // Do the preflight steps
58 $status = $this->preflight->preflight($argv);
60 // If preflight signals that we are done, then exit early.
61 if ($status !== false) {
65 $commandfileSearchpath = $this->preflight->getCommandFilePaths();
66 $this->preflight->logger()->log('Commandfile search paths: ' . implode(',', $commandfileSearchpath));
67 $this->preflight->config()->set('runtime.commandfile.paths', $commandfileSearchpath);
69 // Require the Composer autoloader for Drupal (if different)
70 $loader = $this->preflight->loadSiteAutoloader();
72 // Create the Symfony Application et. al.
73 $input = $this->preflight->createInput();
74 $output = new \Symfony\Component\Console\Output\ConsoleOutput();
75 $application = new \Drush\Application('Drush Commandline Tool', Drush::getVersion());
77 // Set up the DI container.
78 $container = DependencyInjection::initContainer(
80 $this->preflight->config(),
84 $this->preflight->drupalFinder(),
85 $this->preflight->aliasManager()
88 // Now that the DI container has been set up, the Application object will
89 // have a reference to the bootstrap manager et. al., so we may use it
90 // as needed. Tell the application to coordinate between the Bootstrap
91 // manager and the alias manager to select a more specific URI, if
92 // one was not explicitly provided earlier in the preflight.
93 $application->refineUriSelection($this->preflight->environment()->cwd());
95 // Our termination handlers depend on classes we set up via DependencyInjection,
96 // so we do not want to enable it any earlier than this.
97 // TODO: Inject a termination handler into this class, so that we don't
98 // need to add these e.g. when testing.
99 $this->setTerminationHandlers();
101 // Add global options and copy their values into Config.
102 $application->configureGlobalOptions();
104 // Configure the application object and register all of the commandfiles
105 // from the search paths we found above. After this point, the input
106 // and output objects are ready & we can start using the logger, etc.
107 $application->configureAndRegisterCommands($input, $output, $commandfileSearchpath);
109 // Run the Symfony Application
110 // Predispatch: call a remote Drush command if applicable (via a 'pre-init' hook)
111 // Bootstrap: bootstrap site to the level requested by the command (via a 'post-init' hook)
112 $status = $application->run($input, $output);
114 // Placate the Drush shutdown handler.
115 // TODO: use a more modern termination management strategy
116 drush_set_context('DRUSH_EXECUTION_COMPLETED', true);
118 // For backwards compatibility (backend invoke needs this in drush_backend_output())
119 drush_set_context('DRUSH_ERROR_CODE', $status);
125 * Make sure we are notified on exit, and when bad things happen.
127 protected function setTerminationHandlers()
129 // Set an error handler and a shutdown function
130 // TODO: move these to a class somewhere
131 set_error_handler('drush_error_handler');
132 register_shutdown_function('drush_shutdown');