diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index c9fa2ec1b6ce0e3acf15b794c6a0aa30f2d01319..ad8cdc93fb4b55fc01c6c768de514c1486966730 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, $config_directories, $config;
+  global $base_url, $cookie_domain, $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::setMultipleConnectionInfo($databases);
   // Initialize Settings.
   new Settings($settings);
 }
@@ -1771,7 +1774,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 1f88d4a0cdbe555a401cbd13acd449a39c11ecb2..a45ed2d700513fe39701221ae832717d406de9ab 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -511,7 +511,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());
     }
   }
@@ -1075,9 +1075,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)) {
@@ -1098,8 +1097,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';
 
@@ -1108,25 +1105,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();
@@ -1202,7 +1207,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.
@@ -1214,18 +1218,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 e3eaa6e8015d2a2fe44c19f3b60b855b8ef36152..4a6fb06015f1c5fcd74eb784dc9234edfd2db7e1 100644
--- a/core/lib/Drupal/Core/Database/Database.php
+++ b/core/lib/Drupal/Core/Database/Database.php
@@ -52,7 +52,7 @@ abstract class Database {
    *
    * @var array
    */
-  static protected $databaseInfo = NULL;
+  static protected $databaseInfo = array();
 
   /**
    * A list of key/target credentials to simply ignore.
@@ -85,9 +85,9 @@ abstract class Database {
   /**
    * Starts logging a given logging key on the specified connection.
    *
-   * @param $logging_key
+   * @param string $logging_key
    *   The logging key to log.
-   * @param $key
+   * @param string $key
    *   The database connection key for which we want to log.
    *
    * @return \Drupal\Core\Database\Log
@@ -122,9 +122,9 @@ final public static function startLog($logging_key, $key = 'default') {
    * it again (which does nothing to an open log key) and call methods on it as
    * desired.
    *
-   * @param $logging_key
+   * @param string $logging_key
    *   The logging key to log.
-   * @param $key
+   * @param string $key
    *   The database connection key for which we want to log.
    *
    * @return array
@@ -144,9 +144,9 @@ final public static function getLog($logging_key, $key = 'default') {
   /**
    * Gets the connection object for the specified database key and target.
    *
-   * @param $target
+   * @param string $target
    *   The database target name.
-   * @param $key
+   * @param string $key
    *   The database connection key. Defaults to NULL which means the active key.
    *
    * @return \Drupal\Core\Database\Connection
@@ -179,7 +179,7 @@ final public static function getConnection($target = 'default', $key = NULL) {
    * Note that this method will return FALSE if no connection has been
    * established yet, even if one could be.
    *
-   * @return
+   * @return bool
    *   TRUE if there is at least one database connection established, FALSE
    *   otherwise.
    */
@@ -190,14 +190,10 @@ final public static function isActiveConnection() {
   /**
    * Sets the active connection to the specified key.
    *
-   * @return
+   * @return string|null
    *   The previous database connection key.
    */
   final public static function setActiveConnection($key = 'default') {
-    if (empty(self::$databaseInfo)) {
-      self::parseConnectionInfo();
-    }
-
     if (!empty(self::$databaseInfo[$key])) {
       $old_key = self::$activeKey;
       self::$activeKey = $key;
@@ -207,56 +203,40 @@ final public static function setActiveConnection($key = 'default') {
 
   /**
    * Process the configuration file for database information.
+   *
+   * @param array $info
+   *   The database connection information, as defined in settings.php. The
+   *   structure of this array depends on the database driver it is connecting
+   *   to.
    */
-  final public static function parseConnectionInfo() {
-    global $databases;
-
-    $database_info = is_array($databases) ? $databases : array();
-    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
-        // possible connections for this target. Pick one at random. That allows
-        //  us to have, for example, multiple slave servers.
-        if (empty($value['driver'])) {
-          $database_info[$index][$target] = $database_info[$index][$target][mt_rand(0, count($database_info[$index][$target]) - 1)];
-        }
-
-        // Parse the prefix information.
-        if (!isset($database_info[$index][$target]['prefix'])) {
-          // Default to an empty prefix.
-          $database_info[$index][$target]['prefix'] = array(
-            'default' => '',
-          );
-        }
-        elseif (!is_array($database_info[$index][$target]['prefix'])) {
-          // Transform the flat form into an array form.
-          $database_info[$index][$target]['prefix'] = array(
-            'default' => $database_info[$index][$target]['prefix'],
-          );
-        }
-      }
+  final public static function parseConnectionInfo(array $info) {
+    // If there is no "driver" property, then we assume it's an array of
+    // possible connections for this target. Pick one at random. That allows
+    // us to have, for example, multiple slave servers.
+    if (empty($info['driver'])) {
+      $info = $info[mt_rand(0, count($info) - 1)];
     }
-
-    if (!is_array(self::$databaseInfo)) {
-      self::$databaseInfo = $database_info;
+    // Parse the prefix information.
+    if (!isset($info['prefix'])) {
+      // Default to an empty prefix.
+      $info['prefix'] = array(
+        'default' => '',
+      );
     }
-
-    // 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.
-    else {
-      foreach ($database_info as $database_key => $database_values) {
-        foreach ($database_values as $target => $target_values) {
-          self::$databaseInfo[$database_key][$target] = $target_values;
-        }
-      }
+    elseif (!is_array($info['prefix'])) {
+      // Transform the flat form into an array form.
+      $info['prefix'] = array(
+        'default' => $info['prefix'],
+      );
     }
+    return $info;
   }
 
   /**
    * Adds database connection information for a given key/target.
    *
-   * This method allows the addition of new connection credentials at runtime.
+   * This method allows to add new connections at runtime.
+   *
    * Under normal circumstances the preferred way to specify database
    * credentials is via settings.php. However, this method allows them to be
    * added at arbitrary times, such as during unit tests, when connecting to
@@ -264,52 +244,71 @@ final public static function parseConnectionInfo() {
    *
    * If the given key/target pair already exists, this method will be ignored.
    *
-   * @param $key
+   * @param string $key
    *   The database key.
-   * @param $target
+   * @param string $target
    *   The database target name.
-   * @param $info
-   *   The database connection information, as it would be defined in
-   *   settings.php. Note that the structure of this array will depend on the
-   *   database driver it is connecting to.
+   * @param array $info
+   *   The database connection information, as defined in settings.php. The
+   *   structure of this array depends on the database driver it is connecting
+   *   to.
    */
-  public static function addConnectionInfo($key, $target, $info) {
+  final public static function addConnectionInfo($key, $target, array $info) {
     if (empty(self::$databaseInfo[$key][$target])) {
-      self::$databaseInfo[$key][$target] = $info;
+      self::$databaseInfo[$key][$target] = self::parseConnectionInfo($info);
     }
   }
 
   /**
    * Gets information on the specified database connection.
    *
-   * @param $connection
-   *   The connection key for which we want information.
+   * @param string $key
+   *   (optional) The connection key for which to return information.
+   *
+   * @return array|null
    */
   final public static function getConnectionInfo($key = 'default') {
-    if (empty(self::$databaseInfo)) {
-      self::parseConnectionInfo();
-    }
-
     if (!empty(self::$databaseInfo[$key])) {
       return self::$databaseInfo[$key];
     }
   }
 
+  /**
+   * Gets connection information for all available databases.
+   *
+   * @return array
+   */
+  final public static function getAllConnectionInfo() {
+    return self::$databaseInfo;
+  }
+
+  /**
+   * Sets connection information for multiple databases.
+   *
+   * @param array $databases
+   *   A multi-dimensional array specifying database connection parameters, as
+   *   defined in settings.php.
+   */
+  final public static function setMultipleConnectionInfo(array $databases) {
+    foreach ($databases as $key => $targets) {
+      foreach ($targets as $target => $info) {
+        self::addConnectionInfo($key, $target, $info);
+      }
+    }
+  }
+
   /**
    * Rename a connection and its corresponding connection information.
    *
-   * @param $old_key
+   * @param string $old_key
    *   The old connection key.
-   * @param $new_key
+   * @param string $new_key
    *   The new connection key.
-   * @return
+   *
+   * @return bool
    *   TRUE in case of success, FALSE otherwise.
    */
   final public static function renameConnection($old_key, $new_key) {
-    if (empty(self::$databaseInfo)) {
-      self::parseConnectionInfo();
-    }
-
     if (!empty(self::$databaseInfo[$old_key]) && empty(self::$databaseInfo[$new_key])) {
       // Migrate the database connection information.
       self::$databaseInfo[$new_key] = self::$databaseInfo[$old_key];
@@ -331,9 +330,10 @@ final public static function renameConnection($old_key, $new_key) {
   /**
    * Remove a connection and its corresponding connection information.
    *
-   * @param $key
+   * @param string $key
    *   The connection key.
-   * @return
+   *
+   * @return bool
    *   TRUE in case of success, FALSE otherwise.
    */
   final public static function removeConnection($key) {
@@ -350,20 +350,16 @@ final public static function removeConnection($key) {
   /**
    * Opens a connection to the server specified by the given key and target.
    *
-   * @param $key
+   * @param string $key
    *   The database connection key, as specified in settings.php. The default is
    *   "default".
-   * @param $target
+   * @param string $target
    *   The database target to open.
    *
    * @throws \Drupal\Core\Database\ConnectionNotDefinedException
    * @throws \Drupal\Core\Database\DriverNotSpecifiedException
    */
   final protected static function openConnection($key, $target) {
-    if (empty(self::$databaseInfo)) {
-      self::parseConnectionInfo();
-    }
-
     // If the requested database does not exist then it is an unrecoverable
     // error.
     if (!isset(self::$databaseInfo[$key])) {
@@ -399,10 +395,10 @@ final protected static function openConnection($key, $target) {
   /**
    * Closes a connection to the server specified by the given key and target.
    *
-   * @param $target
+   * @param string $target
    *   The database target name.  Defaults to NULL meaning that all target
    *   connections will be closed.
-   * @param $key
+   * @param string $key
    *   The database connection key. Defaults to NULL which means the active key.
    */
   public static function closeConnection($target = NULL, $key = NULL) {
@@ -439,9 +435,9 @@ public static function closeConnection($target = NULL, $key = NULL) {
    * method with the database key and the target to disable. That database key
    * will then always fall back to 'default' for that key, even if it's defined.
    *
-   * @param $key
+   * @param string $key
    *   The database connection key.
-   * @param $target
+   * @param string $target
    *   The target of the specified key to ignore.
    */
   public static function ignoreTarget($key, $target) {
diff --git a/core/modules/migrate/lib/Drupal/migrate/Tests/MigrateTestBase.php b/core/modules/migrate/lib/Drupal/migrate/Tests/MigrateTestBase.php
index b8a5e52259cb677d2f645f0c31d9af326396fffe..ca3a6d52724e4cf47ecfc343254e92f9c855dcc1 100644
--- a/core/modules/migrate/lib/Drupal/migrate/Tests/MigrateTestBase.php
+++ b/core/modules/migrate/lib/Drupal/migrate/Tests/MigrateTestBase.php
@@ -40,7 +40,7 @@ protected function prepare(MigrationInterface $migration, array $files = array()
     $database = SqlBase::getDatabaseConnection($migration->id(), array('database' => $connection_info['default']));
     foreach (array('source', 'destination', 'idMap') as $key) {
       $configuration = $migration->get($key);
-      $configuration['database'] = $database;
+      $configuration['database'] = $connection_info['default'];
       $migration->set($key, $configuration);
     }
 
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index e1bcc90234f119da2934e70b122ef09d525418a5..27659eccc979d8e2ad0fcdcbf1430ab7fe8b8c84 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -933,7 +933,11 @@ 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;
+      // Replace the full table prefix definition to ensure that no table
+      // prefixes of the test runner leak into the test.
+      $connection_info[$target]['prefix'] = array(
+        'default' => $value['prefix']['default'] . $this->databasePrefix,
+      );
     }
     Database::addConnectionInfo('default', 'default', $connection_info['default']);
   }
@@ -1168,11 +1172,10 @@ private function restoreEnvironment() {
     usleep(50000);
 
     // Remove all prefixed tables.
-    // @todo Connection prefix info is not normalized into an array.
     $original_connection_info = Database::getConnectionInfo('simpletest_original_default');
-    $original_prefix = is_array($original_connection_info['default']['prefix']) ? $original_connection_info['default']['prefix']['default'] : $original_connection_info['default']['prefix'];
+    $original_prefix = $original_connection_info['default']['prefix']['default'];
     $test_connection_info = Database::getConnectionInfo('default');
-    $test_prefix = is_array($test_connection_info['default']['prefix']) ? $test_connection_info['default']['prefix']['default'] : $test_connection_info['default']['prefix'];
+    $test_prefix = $test_connection_info['default']['prefix']['default'];
     if ($original_prefix != $test_prefix) {
       $tables = Database::getConnection()->schema()->findTables($test_prefix . '%');
       $prefix_length = strlen($test_prefix);
@@ -1193,10 +1196,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/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 97954558159d2cb55b9faa0a03d579c8dc423df0..15f76a29adb9307e7de8933a2ab009c256247405 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -892,6 +892,7 @@ protected function setUp() {
   protected function installParameters() {
     $connection_info = Database::getConnectionInfo();
     $driver = $connection_info['default']['driver'];
+    $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default'];
     unset($connection_info['default']['driver']);
     unset($connection_info['default']['namespace']);
     unset($connection_info['default']['pdo']);
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 430a1b6d43d9ad21e7c6904ae60d051e5e732cad..cb8b01fb5c966fdedb54c66cd700d42dc347615f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php
@@ -52,8 +52,6 @@ function setUp() {
     // 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');
   }