diff --git a/core/core.services.yml b/core/core.services.yml index 1ee07fe..b7507aa 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1172,6 +1172,11 @@ services: tags: - { name: event_subscriber } arguments: ['@config.manager', '@config.storage', '@config.storage.snapshot'] + exception.needs_installer: + class: Drupal\Core\EventSubscriber\ExceptionDetectNeedsInstallSubscriber + arguments: ['@database'] + tags: + - { name: event_subscriber } exception.default_json: class: Drupal\Core\EventSubscriber\ExceptionJsonSubscriber tags: diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 791bf7a..08d64ad 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -1419,6 +1419,33 @@ public function quote($string, $parameter_type = \PDO::PARAM_STR) { } /** + * Checks if there the database is empty. + * + * @return bool + * Returns TRUE if there is an empty database for the connection. + */ + public function databaseEmpty() { + // Check for the presence of a database table provided in the hook_schema() + // implementation of a required core module. That should always be present + // if Drupal is already installed. + try { + return !$this->schema()->tableExists('sessions'); + } + catch (\Exception $e) { + // If we still have an exception at this point, we need to be careful + // since we should not redirect if the exception represents an error on an + // already-installed site (for example, if the database server went down). + // However a database error with code 1049 means "missing database", and + // it should be safe to redirect in that case. + return ($e instanceof \PDOException || $e instanceof DatabaseException) && $e->getCode() == 1049; + } + + // Default to FALSE to minimize the chance of overwriting an installed + // database. + return FALSE; + } + + /** * Prevents the database connection from being serialized. */ public function __sleep() { diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 1c8068b..e39adde 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -16,6 +16,7 @@ use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\File\MimeType\MimeTypeGuesser; use Drupal\Core\Http\TrustedHostsRequestFactory; +use Drupal\Core\Installer\CheckInstalledTrait; use Drupal\Core\Language\Language; use Drupal\Core\Site\Settings; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -44,6 +45,7 @@ * container, or modify existing services. */ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { + use CheckInstalledTrait; /** * Holds the class used for dumping the container to a PHP array. @@ -647,7 +649,7 @@ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = // installed yet (i.e., if no $databases array has been defined in the // settings.php file) and we are not already installing. if (!Database::getConnectionInfo() && !drupal_installation_attempted() && PHP_SAPI !== 'cli') { - $response = new RedirectResponse($request->getBasePath() . '/core/install.php'); + $response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']); } else { $this->boot(); @@ -686,14 +688,17 @@ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = * If the passed in exception cannot be turned into a response. */ protected function handleException(\Exception $e, $request, $type) { + if ($this->shouldRedirectToInstaller($e, $this->container ? $this->container->get('database') : NULL)) { + return new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache']); + } + if ($e instanceof HttpExceptionInterface) { $response = new Response($e->getMessage(), $e->getStatusCode()); $response->headers->add($e->getHeaders()); return $response; } - else { - throw $e; - } + + throw $e; } /** diff --git a/core/lib/Drupal/Core/EventSubscriber/ExceptionDetectNeedsInstallSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionDetectNeedsInstallSubscriber.php new file mode 100644 index 0000000..b94a0dc --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionDetectNeedsInstallSubscriber.php @@ -0,0 +1,65 @@ +connection = $connection; + } + + /** + * Handles errors for this subscriber. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function onException(GetResponseForExceptionEvent $event) { + $exception = $event->getException(); + if ($this->shouldRedirectToInstaller($exception, $this->connection)) { + // Only redirect if this is an HTML response (i.e., a user trying to view + // the site in a web browser before installing it). + $request = $event->getRequest(); + $format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat()); + if ($format == 'html') { + $event->setResponse(new RedirectResponse($request->getBasePath() . '/core/install.php', 302, ['Cache-Control' => 'no-cache'])); + } + } + } + + /** + * Registers the methods in this class that should be listeners. + * + * @return array + * An array of event listener definitions. + */ + public static function getSubscribedEvents() { + $events[KernelEvents::EXCEPTION][] = ['onException', 100]; + return $events; + } + +} diff --git a/core/lib/Drupal/Core/Installer/CheckInstalledTrait.php b/core/lib/Drupal/Core/Installer/CheckInstalledTrait.php new file mode 100644 index 0000000..f5fa91d --- /dev/null +++ b/core/lib/Drupal/Core/Installer/CheckInstalledTrait.php @@ -0,0 +1,61 @@ +databaseEmpty(); + } + } + +}