diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index fe34fc57547a19bba5c3eb200f67a75d9d6caff6..d00f819d72ec450504cdb01af185dc4f416e56ac 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -405,7 +405,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();
 
@@ -414,6 +415,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);
 }
@@ -1598,7 +1601,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 25055945f95e250ff0c89741d1cad4f2693df2d7..b00d23b21d3829dbd7f88690dbb1edd88d363f2e 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -431,7 +431,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 AlreadyInstalledException($container->get('string_translation'));
     }
   }
@@ -1006,9 +1006,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)) {
@@ -1022,7 +1021,6 @@ function install_verify_database_settings() {
  * Checks a database connection and returns any errors.
  */
 function install_database_errors($database, $settings_file) {
-  global $databases;
   $errors = array();
 
   // Check database type.
@@ -1034,18 +1032,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/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php
index 0d4f599e58d8b0636808d530049614a2815c74fa..23f0d03151845311b51d1b7622e534fe3726fe3c 100644
--- a/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php
+++ b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Installer\Form;
 
 use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Database\Database;
 use Drupal\Core\Form\FormBase;
 
 /**
@@ -26,8 +27,6 @@ public function getFormId() {
    * {@inheritdoc}
    */
   public function buildForm(array $form, array &$form_state) {
-    global $databases;
-
     $conf_path = './' . conf_path(FALSE);
     $settings_file = $conf_path . '/settings.php';
 
@@ -36,25 +35,33 @@ public function buildForm(array $form, array &$form_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'];
+    if (!isset($form_state['input']['driver']) && $database = Database::getConnectionInfo()) {
+      $form_state['input']['driver'] = $database['default']['driver'];
+      $form_state['input'][$database['default']['driver']] = $database['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'])) {
       $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();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index d3c3bf96cc500822302c522576c379e822acd387..5af009876c2a5b0bc839c5cdab121040e4c9724b 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -958,7 +958,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']);
   }
@@ -1162,11 +1166,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);
@@ -1187,10 +1190,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'];
 
     // Reset all static variables.
     // All destructors of statically cached objects have been invoked above;
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index b91aeb627c736168e321ab9b81bae1e567cb07d4..7243186c59875905daea1dab47b99ece6d554189 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -936,6 +936,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');
   }
 
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 3fc16ef01d126078e3b901f2aa8f49a1dc8efa7f..3134a5c534db665095daab8402774edfafacb86c 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -131,11 +131,13 @@ function simpletest_script_help() {
               sub-processes. However, you may use e.g. '/tmpfs/test.sqlite'
 
   --dburl     A URI denoting the database driver, credentials, server hostname,
-              and database name to use in tests. For example:
-                mysql://username:password@localhost/databasename#table_prefix
-              Only used if specified.
+              and database name to use in tests.
               Required when running tests without a Drupal installation that
               contains default database connection info in settings.php.
+              Examples:
+                mysql://username:password@localhost/databasename#table_prefix
+                sqlite://localhost/relative/path/db.sqlite
+                sqlite://localhost//absolute/path/db.sqlite
 
   --php       The absolute path to the PHP executable. Usually not needed.
 
@@ -431,7 +433,7 @@ function simpletest_script_bootstrap() {
  *   connections are prepared only.
  */
 function simpletest_script_setup_database($new = FALSE) {
-  global $args, $databases;
+  global $args;
 
   // If there is an existing Drupal installation that contains a database
   // connection info in settings.php, then $databases['default']['default'] will
@@ -443,7 +445,6 @@ function simpletest_script_setup_database($new = FALSE) {
   // connection can be set and/or overridden with the --dburl parameter.
   if (!empty($args['dburl'])) {
     // Remove a possibly existing default connection (from settings.php).
-    unset($databases['default']);
     Database::removeConnection('default');
 
     $info = parse_url($args['dburl']);
@@ -456,25 +457,26 @@ function simpletest_script_setup_database($new = FALSE) {
       'pass' => '',
       'fragment' => '',
     );
+    if ($info['path'][0] === '/') {
+      $info['path'] = substr($info['path'], 1);
+    }
+    if ($info['scheme'] === 'sqlite' && $info['path'][0] !== '/') {
+      $info['path'] = DRUPAL_ROOT . '/' . $info['path'];
+    }
     $databases['default']['default'] = array(
       'driver' => $info['scheme'],
       'username' => $info['user'],
       'password' => $info['pass'],
       'host' => $info['host'],
-      'database' => ltrim($info['path'], '/'),
+      'database' => $info['path'],
       'prefix' => array(
         'default' => $info['fragment'],
       ),
     );
   }
-  // Otherwise, ensure that database table prefix info is an array.
-  // @see https://drupal.org/node/2176621
-  elseif (isset($databases['default']['default'])) {
-    if (!is_array($databases['default']['default']['prefix'])) {
-      $databases['default']['default']['prefix'] = array(
-        'default' => $databases['default']['default']['prefix'],
-      );
-    }
+  // Otherwise, use the default database connection from settings.php.
+  else {
+    $databases['default'] = Database::getConnectionInfo('default');
   }
 
   // If there is no default database connection for tests, we cannot continue.