preflight = $preflight; } /** * Run the application, catching any errors that may be thrown. * Typically, this will happen only for code that fails fast during * preflight. Later code should catch and handle its own exceptions. */ public function run($argv) { try { $status = $this->doRun($argv); } catch (\Exception $e) { $status = $e->getCode(); $message = $e->getMessage(); // Uncaught exceptions could happen early, before our logger // and other classes are initialized. Print them and exit. $this->preflight->logger()->setDebug(true)->log($message); } return $status; } /** * Start up Drush */ protected function doRun($argv) { // Do the preflight steps $status = $this->preflight->preflight($argv); // If preflight signals that we are done, then exit early. if ($status !== false) { return $status; } $commandfileSearchpath = $this->preflight->getCommandFilePaths(); $this->preflight->logger()->log('Commandfile search paths: ' . implode(',', $commandfileSearchpath)); $this->preflight->config()->set('runtime.commandfile.paths', $commandfileSearchpath); // Require the Composer autoloader for Drupal (if different) $loader = $this->preflight->loadSiteAutoloader(); // Create the Symfony Application et. al. $input = $this->preflight->createInput(); $output = new \Symfony\Component\Console\Output\ConsoleOutput(); $application = new \Drush\Application('Drush Commandline Tool', Drush::getVersion()); // Set up the DI container. $container = DependencyInjection::initContainer( $application, $this->preflight->config(), $input, $output, $loader, $this->preflight->drupalFinder(), $this->preflight->aliasManager() ); // Now that the DI container has been set up, the Application object will // have a reference to the bootstrap manager et. al., so we may use it // as needed. Tell the application to coordinate between the Bootstrap // manager and the alias manager to select a more specific URI, if // one was not explicitly provided earlier in the preflight. $application->refineUriSelection($this->preflight->environment()->cwd()); // Our termination handlers depend on classes we set up via DependencyInjection, // so we do not want to enable it any earlier than this. // TODO: Inject a termination handler into this class, so that we don't // need to add these e.g. when testing. $this->setTerminationHandlers(); // Add global options and copy their values into Config. $application->configureGlobalOptions(); // Configure the application object and register all of the commandfiles // from the search paths we found above. After this point, the input // and output objects are ready & we can start using the logger, etc. $application->configureAndRegisterCommands($input, $output, $commandfileSearchpath); // Run the Symfony Application // Predispatch: call a remote Drush command if applicable (via a 'pre-init' hook) // Bootstrap: bootstrap site to the level requested by the command (via a 'post-init' hook) $status = $application->run($input, $output); // Placate the Drush shutdown handler. // TODO: use a more modern termination management strategy drush_set_context('DRUSH_EXECUTION_COMPLETED', true); // For backwards compatibility (backend invoke needs this in drush_backend_output()) drush_set_context('DRUSH_ERROR_CODE', $status); return $status; } /** * Make sure we are notified on exit, and when bad things happen. */ protected function setTerminationHandlers() { // Set an error handler and a shutdown function // TODO: move these to a class somewhere set_error_handler('drush_error_handler'); register_shutdown_function('drush_shutdown'); } }