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.