diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index d92e27c6..e9a1a56 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -491,7 +491,8 @@ function drupal_valid_http_host($host) {
  */
 function drupal_settings_initialize() {
   // Export these settings.php variables to the global namespace.
-  global $base_url, $databases, $cookie_domain, $drupal_hash_salt, $config_directories, $config;
+  global $base_url, $cookie_domain, $drupal_hash_salt, $config_directories, $config;
+  $databases = array();
   $settings = array();
   $config = array();
 
@@ -500,6 +501,8 @@ function drupal_settings_initialize() {
   if (is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) {
     require DRUPAL_ROOT . '/' . $conf_path . '/settings.php';
   }
+  // Initialize Database.
+  Database::setConnectionInfo($databases);
   // Initialize Settings.
   new Settings($settings);
 }
@@ -1662,10 +1665,13 @@ function drupal_get_user_timezone() {
  *   A salt based on information in settings.php, not in the database.
  */
 function drupal_get_hash_salt() {
-  global $drupal_hash_salt, $databases;
-  // If the $drupal_hash_salt variable is empty, a hash of the serialized
-  // database credentials is used as a fallback salt.
-  return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
+  global $drupal_hash_salt;
+  // This should never happen, as it breaks user logins and many other services.
+  // Therefore, explicitly notify the user (developer) by throwing an exception.
+  if (empty($drupal_hash_salt)) {
+    throw new \RuntimeException('Missing $drupal_hash_salt in settings.php.');
+  }
+  return $drupal_hash_salt;
 }
 
 /**
@@ -1762,7 +1768,7 @@ function _drupal_bootstrap_configuration() {
   // Redirect the user to the installation script if Drupal has not been
   // installed yet (i.e., if no $databases array has been defined in the
   // settings.php file) and we are not already installing.
-  if (empty($GLOBALS['databases']) && !drupal_installation_attempted() && !drupal_is_cli()) {
+  if (!Database::getConnectionInfo() && !drupal_installation_attempted() && !drupal_is_cli()) {
     include_once __DIR__ . '/install.inc';
     install_goto('core/install.php');
   }
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index a3ed418..1a989c1 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -512,7 +512,7 @@ function install_begin_request(&$install_state) {
     $task = NULL;
 
     // Do not install over a configured settings.php.
-    if (!empty($GLOBALS['databases'])) {
+    if (Database::getConnectionInfo()) {
       throw new Exception(install_already_done_error());
     }
   }
@@ -1076,9 +1076,8 @@ function install_verify_completed_task() {
  * Verifies that settings.php specifies a valid database connection.
  */
 function install_verify_database_settings() {
-  global $databases;
-  if (!empty($databases)) {
-    $database = $databases['default']['default'];
+  if ($database = Database::getConnectionInfo()) {
+    $database = $database['default'];
     $settings_file = './' . conf_path(FALSE) . '/settings.php';
     $errors = install_database_errors($database, $settings_file);
     if (empty($errors)) {
@@ -1099,8 +1098,6 @@ function install_verify_database_settings() {
  * @ingroup forms
  */
 function install_settings_form($form, &$form_state, &$install_state) {
-  global $databases;
-
   $conf_path = './' . conf_path(FALSE);
   $settings_file = $conf_path . '/settings.php';
 
@@ -1109,25 +1106,33 @@ function install_settings_form($form, &$form_state, &$install_state) {
   $drivers = drupal_get_database_types();
   $drivers_keys = array_keys($drivers);
 
-  // If database connection settings have been prepared in settings.php already,
-  // then the existing values need to be taken over.
+  // Unless there is input for this form (for a non-interactive installation,
+  // input originates from the $settings array passed into install_drupal()),
+  // check whether database connection settings have been prepared in
+  // settings.php already.
   // Note: The installer even executes this form if there is a valid database
   // connection already, since the submit handler of this form is responsible
   // for writing all $settings to settings.php (not limited to $databases).
-  if (isset($databases['default']['default'])) {
-    $default_driver = $databases['default']['default']['driver'];
-    $default_options = $databases['default']['default'];
-  }
-  // Otherwise, use the database connection settings from the form input.
-  // For a non-interactive installation, this is derived from the original
-  // $settings array passed into install_drupal().
-  elseif (isset($form_state['input']['driver'])) {
+  if (!isset($form_state['input']['driver']) && $database = Database::getConnectionInfo()) {
+    $form_state['input']['driver'] = $database['default']['driver'];
+    $form_state['input'][$database['default']['driver']] = $database['default'];
+  }
+
+  if (isset($form_state['input']['driver'])) {
     $default_driver = $form_state['input']['driver'];
+    // In case of database connection info from settings.php, as well as for a
+    // programmed form submission (non-interactive installer), the table prefix
+    // information is usually normalized into an array already, but the form
+    // element only allows to configure one default prefix for all tables.
+    $prefix = &$form_state['input'][$default_driver]['prefix'];
+    if (isset($prefix) && is_array($prefix)) {
+      $prefix = $prefix['default'];
+    }
     $default_options = $form_state['input'][$default_driver];
   }
-  // If there is no database information at all yet, just suggest the first
-  // available driver as default value, so that its settings form is made
-  // visible via #states when JavaScript is enabled (see below).
+  // If there is no database information yet, suggest the first available driver
+  // as default value, so that its settings form is made visible via #states
+  // when JavaScript is enabled (see below).
   else {
     $default_driver = current($drivers_keys);
     $default_options = array();
@@ -1203,7 +1208,6 @@ function install_settings_form_validate($form, &$form_state) {
  * Checks a database connection and returns any errors.
  */
 function install_database_errors($database, $settings_file) {
-  global $databases;
   $errors = array();
 
   // Check database type.
@@ -1215,18 +1219,13 @@ function install_database_errors($database, $settings_file) {
   else {
     // Run driver specific validation
     $errors += $database_types[$driver]->validateDatabaseSettings($database);
-
+    if (!empty($errors)) {
+      // No point to try further.
+      return $errors;
+    }
     // Run tasks associated with the database type. Any errors are caught in the
     // calling function.
-    $databases['default']['default'] = $database;
-    // Just changing the global doesn't get the new information processed.
-    // We need to close any active connections and tell the Database class to
-    // re-parse $databases.
-    if (Database::isActiveConnection()) {
-      Database::closeConnection();
-    }
-    Database::parseConnectionInfo();
-
+    Database::addConnectionInfo('default', 'default', $database);
     try {
       db_run_tasks($driver);
     }
diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php
index e3eaa6e..dd6c59a 100644
--- a/core/lib/Drupal/Core/Database/Database.php
+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -48,6 +48,13 @@
   static protected $connections = array();
 
   /**
+   * The unprocessed database connection information from settings.php.
+   *
+   * @var array
+   */
+  static protected $rawDatabaseInfo = NULL;
+
+  /**
    * A processed copy of the database connection information from settings.php.
    *
    * @var array
@@ -194,6 +201,9 @@
    *   The previous database connection key.
    */
   final public static function setActiveConnection($key = 'default') {
+    if (empty(self::$rawDatabaseInfo)) {
+      return;
+    }
     if (empty(self::$databaseInfo)) {
       self::parseConnectionInfo();
     }
@@ -209,9 +219,7 @@
    * Process the configuration file for database information.
    */
   final public static function parseConnectionInfo() {
-    global $databases;
-
-    $database_info = is_array($databases) ? $databases : array();
+    $database_info = self::$rawDatabaseInfo;
     foreach ($database_info as $index => $info) {
       foreach ($database_info[$index] as $target => $value) {
         // If there is no "driver" property, then we assume it's an array of
@@ -237,10 +245,9 @@
       }
     }
 
-    if (!is_array(self::$databaseInfo)) {
+    if (empty(self::$databaseInfo)) {
       self::$databaseInfo = $database_info;
     }
-
     // Merge the new $database_info into the existing.
     // array_merge_recursive() cannot be used, as it would make multiple
     // database, user, and password keys in the same database array.
@@ -274,6 +281,8 @@
    *   database driver it is connecting to.
    */
   public static function addConnectionInfo($key, $target, $info) {
+    // @todo $info is not necessarily processed yet, but other code only checks
+    //   for empty(self::$databaseInfo).
     if (empty(self::$databaseInfo[$key][$target])) {
       self::$databaseInfo[$key][$target] = $info;
     }
@@ -282,10 +291,15 @@ public static function addConnectionInfo($key, $target, $info) {
   /**
    * Gets information on the specified database connection.
    *
-   * @param $connection
-   *   The connection key for which we want information.
+   * @param $key
+   *   (optional) The connection key for which we want information.
+   *
+   * @return array
    */
   final public static function getConnectionInfo($key = 'default') {
+    if (empty(self::$rawDatabaseInfo)) {
+      return array();
+    }
     if (empty(self::$databaseInfo)) {
       self::parseConnectionInfo();
     }
@@ -293,6 +307,17 @@ public static function addConnectionInfo($key, $target, $info) {
     if (!empty(self::$databaseInfo[$key])) {
       return self::$databaseInfo[$key];
     }
+    return array();
+  }
+
+  /**
+   * Sets database connection information.
+   *
+   * @param array $databases
+   *   A multi-dimensional array specifying database connection parameters.
+   */
+  final public static function setConnectionInfo(array $databases) {
+    self::$rawDatabaseInfo = $databases;
   }
 
   /**
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index f71e1a2..f6db325 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -919,7 +919,12 @@ private function changeDatabasePrefix() {
     $connection_info = Database::getConnectionInfo('default');
     Database::renameConnection('default', 'simpletest_original_default');
     foreach ($connection_info as $target => $value) {
-      $connection_info[$target]['prefix'] = $value['prefix']['default'] . $this->databasePrefix;
+      // Explicitly replace the entire table prefix definition to prevent a
+      // table-specific prefix of the parent site from being taken over.
+      // @todo Connection prefix info is not normalized into an array.
+      $connection_info[$target]['prefix'] = array(
+        'default' => $value['prefix']['default'] . $this->databasePrefix,
+      );
     }
     Database::addConnectionInfo('default', 'default', $connection_info['default']);
   }
@@ -1174,10 +1179,6 @@ private function restoreEnvironment() {
     // Restore original database connection.
     Database::removeConnection('default');
     Database::renameConnection('simpletest_original_default', 'default');
-    // @see TestBase::changeDatabasePrefix()
-    global $databases;
-    $connection_info = Database::getConnectionInfo('default');
-    $databases['default']['default'] = $connection_info['default'];
 
     // Restore original globals.
     if (isset($this->originalThemeKey)) {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php
index 430a1b6..8029510 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php
@@ -50,10 +50,7 @@ function setUp() {
 
     // Create an additional connection to monitor the connections being opened
     // and closed in this test.
-    // @see TestBase::changeDatabasePrefix()
     Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
-    global $databases;
-    $databases['default']['monitor'] = $connection_info['default'];
     $this->monitor = Database::getConnection('monitor');
   }
 
