diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index e7da6ec..086e8e7 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1,6 +1,7 @@ $name, ':type' => $type))->fetchField(); - if ($file && file_exists(DRUPAL_ROOT . '/' . $file)) { - $files[$type][$name] = $file; + if ($data = KeyValueFactory::get("system.$type")->get($name)) { + $file = $data->filename; + if (file_exists(DRUPAL_ROOT . '/' . $file)) { + $files[$type][$name] = $file; + } } } } @@ -2505,11 +2508,23 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { $container ->register('config.storage.staging', 'Drupal\Core\Config\FileStorage') ->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + $container + ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage') + ->addArgument('state'); } return $container; } /** + * Returns the state storage service. + * + * @return Drupal\Core\KeyValueStore\KeyValueStoreInterface + */ +function state() { + return drupal_container()->get('state.storage'); +} + +/** * Returns the test prefix if this is an internal request from SimpleTest. * * @return diff --git a/core/includes/common.inc b/core/includes/common.inc index d21b9ce..57d5d10 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3176,7 +3176,7 @@ function drupal_pre_render_styles($elements) { function drupal_build_css_cache($css) { $data = ''; $uri = ''; - $map = variable_get('drupal_css_cache_files', array()); + $map = state()->get('drupal_css_cache_files') ?: array(); // Create a new array so that only the file names are used to create the hash. // This prevents new aggregates from being created unnecessarily. $css_data = array(); @@ -3244,7 +3244,7 @@ function drupal_build_css_cache($css) { } // Save the updated map. $map[$key] = $uri; - variable_set('drupal_css_cache_files', $map); + state()->set('drupal_css_cache_files', $map); } return $uri; } @@ -3409,7 +3409,7 @@ function _drupal_load_stylesheet($matches) { * Deletes old cached CSS files. */ function drupal_clear_css_cache() { - variable_del('drupal_css_cache_files'); + state()->delete('drupal_css_cache_files'); file_scan_directory('public://css', '/.*/', array('callback' => 'drupal_delete_file_if_stale')); } @@ -4719,7 +4719,7 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro function drupal_build_js_cache($files) { $contents = ''; $uri = ''; - $map = variable_get('drupal_js_cache_files', array()); + $map = state()->get('drupal_js_cache_files') ?: array(); // Create a new array so that only the file names are used to create the hash. // This prevents new aggregates from being created unnecessarily. $js_data = array(); @@ -4764,7 +4764,7 @@ function drupal_build_js_cache($files) { } } $map[$key] = $uri; - variable_set('drupal_js_cache_files', $map); + state()->set('drupal_js_cache_files', $map); } return $uri; } diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 4a37bc2..ec31f3c 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -311,7 +311,9 @@ function install_begin_request(&$install_state) { $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') ->addArgument(new Reference('config.storage')) ->addArgument(new Reference('dispatcher')); - + $container + ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage') + ->addArgument('state'); drupal_container($container); } @@ -1643,15 +1645,12 @@ function install_import_translations_remaining(&$install_state) { * A message informing the user that the installation is complete. */ function install_finished(&$install_state) { + $profile = drupal_get_profile(); // Remember the profile which was used. - variable_set('install_profile', drupal_get_profile()); + variable_set('install_profile', $profile); // Install profiles are always loaded last. - db_update('system') - ->fields(array('weight' => 1000)) - ->condition('type', 'module') - ->condition('name', drupal_get_profile()) - ->execute(); + module_set_weight($profile, 1000); // Flush all caches to ensure that any full bootstraps during the installer // do not leave stale cached data, and that any content types or other items diff --git a/core/includes/install.inc b/core/includes/install.inc index efba3c1..5c73f0d 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -6,6 +6,7 @@ */ use Drupal\Core\Database\Database; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Drupal\locale\Gettext; /** @@ -420,18 +421,15 @@ function drupal_install_system() { require_once DRUPAL_ROOT . '/' . $system_path . '/system.install'; $system_versions = drupal_get_schema_versions('system'); $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED; - db_insert('system') - ->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap')) - ->values(array( - 'filename' => $system_path . '/system.module', - 'name' => 'system', - 'type' => 'module', - 'owner' => '', - 'status' => 1, - 'schema_version' => $system_version, - 'bootstrap' => 0, - )) - ->execute(); + $module_store = KeyValueFactory::get('system.module'); + $module_store + ->set('system', (object) array( + 'filename' => $system_path . '/system.module', + 'schema_version' => $system_version, + 'bootstrap' => 0, + 'info' => '', + )); + module_config()->set('system', 0)->save(); // Clear out module list and hook implementation statics before calling // system_rebuild_theme_data(). diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 8574dbe..4aeda61 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -451,7 +451,7 @@ function menu_get_item($path = NULL, $router_item = NULL) { if (!isset($router_items[$path])) { // Rebuild if we know it's needed, or if the menu masks are missing which // occurs rarely, likely due to a race condition of multiple rebuilds. - if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { + if (state()->get('menu_rebuild_needed') || !variable_get('menu_masks', array())) { menu_router_rebuild(); } $original_map = arg(NULL, $path); @@ -2666,7 +2666,7 @@ function menu_router_rebuild() { menu_cache_clear_all(); _menu_clear_page_cache(); // Indicate that the menu has been successfully rebuilt. - variable_del('menu_rebuild_needed'); + state()->delete('menu_rebuild_needed'); } catch (Exception $e) { $transaction->rollback(); diff --git a/core/includes/module.inc b/core/includes/module.inc index 5a30875..9c223f8 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -6,6 +6,7 @@ */ use Drupal\Component\Graph\Graph; +use Drupal\Core\KeyValueStore\KeyValueFactory; /** * Loads all the modules that have been enabled in the system table. @@ -128,15 +129,24 @@ function system_list($type) { $bootstrap_list = $cached->data; } else { - $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name'); + $store = KeyValueFactory::get('system.module'); + $enabled_modules = module_config()->get(); + $modules = $store->getMultiple(array_keys($enabled_modules)); + $bootstrap_list = array(); + foreach ($enabled_modules as $name => $weight) { + $record = $modules[$name]; + if ($record->bootstrap) { + $bootstrap_list[$name] = $record; + } + } cache('bootstrap')->set('bootstrap_modules', $bootstrap_list); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache for bootstrap modules only. // The rest is stored separately to keep the bootstrap module cache small. - foreach ($bootstrap_list as $module) { - drupal_classloader_register($module->name, dirname($module->filename)); - drupal_get_filename('module', $module->name, $module->filename); + foreach ($bootstrap_list as $name => $module) { + drupal_classloader_register($name, dirname($module->filename)); + drupal_get_filename('module', $name, $module->filename); } // We only return the module names here since module_list() doesn't need // the filename itself. @@ -158,22 +168,31 @@ function system_list($type) { // Drupal installations, which might have modules installed in different // locations in the file system. The ordering here must also be // consistent with the one used in module_implements(). - $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); - foreach ($result as $record) { - // Build a list of all enabled modules. - if ($record->type == 'module') { - $lists['module_enabled'][$record->name] = $record->name; - } - // Build a list of themes. - if ($record->type == 'theme') { - $record->info = unserialize($record->info); - $lists['theme'][$record->name] = $record; - } + $theme_config = config('system.theme')->get(); + $store = KeyValueFactory::get('system.theme'); + foreach ($store->getAll() as $name => $record) { + $lists['theme'][$name] = $store->get($name); + $status = isset($theme_config[$name]); + $lists['theme'][$name]->status = $status; + $lists['theme'][$name]->name = $name; // Build a list of filenames so drupal_get_filename can use it. - if ($record->status) { - $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); + if ($status) { + $lists['filepaths'][] = array('type' => 'theme', 'name' => $name, 'filepath' => $record->filename); } } + $store = KeyValueFactory::get('system.module'); + $enabled_modules = module_config()->get(); + $modules = $store->getMultiple(array_keys($enabled_modules)); + foreach ($enabled_modules as $name => $weight) { + // Build a list of all enabled modules. + $lists['module_enabled'][$name] = $name; + // Build a list of filenames so drupal_get_filename can use it. + $lists['filepaths'][] = array( + 'type' => 'module', + 'name' => $name, + 'filepath' => $modules[$name]->filename, + ); + } foreach ($lists['theme'] as $key => $theme) { if (!empty($theme->info['base theme'])) { // Make a list of the theme's base themes. @@ -375,27 +394,28 @@ function module_load_all_includes($type, $name = NULL) { * @see hook_modules_enabled() */ function module_enable($module_list, $enable_dependencies = TRUE) { + // Get all module data so we can find dependencies, sort and copy to the + // installed modules config. + $all_modules = system_rebuild_module_data(); if ($enable_dependencies) { - // Get all module data so we can find dependencies and sort. - $module_data = system_rebuild_module_data(); // Create an associative array with weights as values. $module_list = array_flip(array_values($module_list)); while (list($module) = each($module_list)) { - if (!isset($module_data[$module])) { + if (!isset($all_modules[$module])) { // This module is not found in the filesystem, abort. return FALSE; } - if ($module_data[$module]->status) { + if ($all_modules[$module]->status) { // Skip already enabled modules. unset($module_list[$module]); continue; } - $module_list[$module] = $module_data[$module]->sort; + $module_list[$module] = $all_modules[$module]->sort; // Add dependencies to the list, with a placeholder weight. // The new modules will be processed as the while loop continues. - foreach (array_keys($module_data[$module]->requires) as $dependency) { + foreach (array_keys($all_modules[$module]->requires) as $dependency) { if (!isset($module_list[$dependency])) { $module_list[$dependency] = 0; } @@ -418,25 +438,33 @@ function module_enable($module_list, $enable_dependencies = TRUE) { $modules_installed = array(); $modules_enabled = array(); foreach ($module_list as $module) { + $config = module_config(); // Only process modules that are not already enabled. - $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array( - ':type' => 'module', - ':name' => $module)) - ->fetchObject(); - if ($existing->status == 0) { + $module_store = KeyValueFactory::get('system.module'); + $module_data = $module_store->get($module); + $enabled = TRUE; + if (!$module_data) { + $enabled = FALSE; + $file = $all_modules[$module]; + $module_store + ->set($module, (object) array( + 'filename' => $file->uri, + 'schema_version' => SCHEMA_UNINSTALLED, + 'bootstrap' => 0, + 'info' => $file->info, + )); + $config->set($module, 0); + } + elseif (!$config->get($module)) { + $enabled = FALSE; + $config->set($module, isset($module_data->weight) ? $module_data->weight : 0); + } + if (!$enabled) { + $config->save(); // Load the module's code. drupal_load('module', $module); module_load_install($module); - // Update the database and module list to reflect the new module. This - // needs to be done first so that the module's hook implementations, - // hook_schema() in particular, can be called while it is being - // installed. - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'module') - ->condition('name', $module) - ->execute(); // Refresh the module list to include it. system_list_reset(); module_implements_reset(); @@ -543,15 +571,19 @@ function module_disable($module_list, $disable_dependents = TRUE) { $invoke_modules = array(); + $store = KeyValueFactory::get('system.module'); foreach ($module_list as $module) { if (module_exists($module)) { + $config = module_config(); module_load_install($module); module_invoke($module, 'disable'); - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'module') - ->condition('name', $module) - ->execute(); + if ($weight = $config->get($module)) { + $module_data = $store->get($module); + $module_data->weight = $weight; + $store->set($module, $module_data); + } + $config->clear($module); + $config->save(); $invoke_modules[] = $module; watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); } @@ -620,6 +652,7 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) } $storage = drupal_container()->get('config.storage'); + $store = KeyValueFactory::get('system.module'); foreach ($module_list as $module) { // Uninstall the module. module_load_install($module); @@ -633,8 +666,9 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) } watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); - drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); + $store->delete($module); } + drupal_get_installed_schema_version(NULL, TRUE); if (!empty($module_list)) { // Call hook_module_uninstall to let other modules act @@ -1102,3 +1136,29 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $function($data, $context1, $context2); } } + +/** + * Retrieves the module list configuration object. + * + * @return Drupal\Core\Config\ModuleConfig + * A configuration object. + */ +function module_config() { + return drupal_container()->get('config.factory')->get('system.module', 'Drupal\Core\Config\ModuleConfig')->load(); +} + +function module_set_weight($module, $weight) { + $config = module_config(); + if ($config->get($module) !== NULL) { + $config + ->set($module, $weight) + ->save(); + } + else { + $store = KeyValueFactory::get('system.module'); + $module_data = $store->get($module); + debug($module_data); + $module_data->weight = $weight; + $store->set($module, $module_data); + } +} diff --git a/core/includes/path.inc b/core/includes/path.inc index 07aeee5..71c10a3 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -72,7 +72,7 @@ function drupal_lookup_path($action, $path = '', $langcode = NULL) { // Retrieve the path alias whitelist. if (!isset($cache['whitelist'])) { - $cache['whitelist'] = variable_get('path_alias_whitelist', NULL); + $cache['whitelist'] = state()->get('path_alias_whitelist'); if (!isset($cache['whitelist'])) { $cache['whitelist'] = drupal_path_alias_whitelist_rebuild(); } @@ -391,7 +391,7 @@ function drupal_path_alias_whitelist_rebuild($source = NULL) { // When paths are inserted, only rebuild the whitelist if the system path // has a top level component which is not already in the whitelist. if (!empty($source)) { - $whitelist = variable_get('path_alias_whitelist', NULL); + $whitelist = state()->get('path_alias_whitelist'); if (isset($whitelist[strtok($source, '/')])) { return $whitelist; } @@ -404,7 +404,7 @@ function drupal_path_alias_whitelist_rebuild($source = NULL) { foreach ($result as $row) { $whitelist[$row->path] = TRUE; } - variable_set('path_alias_whitelist', $whitelist); + state()->set('path_alias_whitelist', $whitelist); return $whitelist; } diff --git a/core/includes/schema.inc b/core/includes/schema.inc index 7b0c690..785b4d5 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -6,6 +6,7 @@ */ use Drupal\Core\Database\Database; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Drupal\Core\Utility\SchemaCache; /** @@ -173,9 +174,8 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F if (!$versions) { $versions = array(); - $result = db_query("SELECT name, schema_version FROM {system} WHERE type = :type", array(':type' => 'module')); - foreach ($result as $row) { - $versions[$row->name] = $row->schema_version; + foreach (KeyValueFactory::get('system.module')->getAll() as $name => $data) { + $versions[$name] = $data->schema_version; } } @@ -196,11 +196,10 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F * The new schema version. */ function drupal_set_installed_schema_version($module, $version) { - db_update('system') - ->fields(array('schema_version' => $version)) - ->condition('name', $module) - ->execute(); - + $store = KeyValueFactory::get('system.module'); + $module_data = $store->get($module); + $module_data->schema_version = $version; + $store->set($module, $module_data); // Reset the static cache of module schema versions. drupal_get_installed_schema_version(NULL, TRUE); } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7766952..b6bbda3 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -9,6 +9,7 @@ */ use Drupal\Core\Utility\ThemeRegistry; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Drupal\Core\Template\Attribute; /** @@ -1392,17 +1393,16 @@ function theme_render_template($template_file, $variables) { */ function theme_enable($theme_list) { drupal_clear_css_cache(); - + $config = config('system.theme'); foreach ($theme_list as $key) { - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', $key) - ->execute(); - + // The value is not used, it needs to be consisent with + // config('system.module') which uses the value for weight so set all of + // them to the same value. 1 looks like 'enabled' so let's use that. + $config->set($key, 1); // Install default configuration of the theme. config_install_default_config('theme', $key); } + $config->save(); list_themes(TRUE); menu_router_rebuild(); @@ -1429,13 +1429,11 @@ function theme_disable($theme_list) { drupal_clear_css_cache(); + $config = config('system.theme'); foreach ($theme_list as $key) { - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->condition('name', $key) - ->execute(); + $config->clear($key); } + $config->save(); list_themes(TRUE); menu_router_rebuild(); diff --git a/core/includes/update.inc b/core/includes/update.inc index 9dd8456..ada12f1 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -9,6 +9,7 @@ */ use Drupal\Component\Graph\Graph; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\ConfigException; @@ -26,18 +27,18 @@ const REQUIRED_D7_SCHEMA_VERSION = '7069'; * Disable any items in the {system} table that are not core compatible. */ function update_fix_compatibility() { - $incompatible = array(); - $result = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')"); - foreach ($result as $row) { - if (update_check_incompatibility($row->name, $row->type)) { - $incompatible[] = $row->name; + foreach (array('module', 'theme') as $type) { + $config = $type == 'module' ? module_config() : config('system.theme'); + $save = FALSE; + foreach ($config->get() as $name => $weight) { + if (update_check_incompatibility($name, $type)) { + $config->clear($name); + $save = TRUE; + } + } + if ($save) { + $config->save(); } - } - if (!empty($incompatible)) { - db_update('system') - ->fields(array('status' => 0)) - ->condition('name', $incompatible, 'IN') - ->execute(); } } @@ -116,7 +117,12 @@ function update_prepare_d8_bootstrap() { // running an up-to-date version of Drupal 7 before proceeding. Note this has // to happen AFTER the database bootstraps because of // drupal_get_installed_schema_version(). - $system_schema = drupal_get_installed_schema_version('system'); + try { + $system_schema = drupal_get_installed_schema_version('system'); + } + catch (\Exception $e) { + $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(':system' => 'system'))->fetchField(); + } if ($system_schema < 8000) { $has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION; $requirements = array( @@ -130,20 +136,87 @@ function update_prepare_d8_bootstrap() { update_extra_requirements($requirements); if ($has_required_schema) { + if (!db_table_exists('keyvalue')) { + $specs = array( + 'description' => 'Generic key-value storage table.', + 'fields' => array( + 'name' => array( + 'description' => 'The key.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'collection' => array( + 'description' => 'The collection of the variable.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value.', + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'big', + 'translatable' => TRUE, + ), + ), + 'primary key' => array('collection', 'name'), + ); + db_create_table('keyvalue', $specs); + } // Bootstrap variables so we can update theme while preparing the update // process. drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); - // Update the dynamic include paths that might be used before running the - // proper update functions. - update_prepare_stored_includes(); - // Update the environment for the language bootstrap if needed. - update_prepare_d8_language(); // Ensure the configuration directories exist and are writable, or create // them. If the directories have not been specified in settings.php and // created manually already, and either directory cannot be created by the // web server, an exception will be thrown, halting the update. drupal_install_config_directories(); + $stores = array( + 'module' => KeyValueFactory::get('system.module'), + 'theme' => KeyValueFactory::get('system.theme'), + ); + $configs = array( + 'module' => module_config(), + 'theme' => config('system.theme'), + ); + $keys = array( + 'module' => array('filename', 'schema_version', 'bootstrap'), + 'theme' => array('filename', 'owner'), + ); + $result = db_query('SELECT * FROM {system} WHERE type = :theme OR (type = :module AND schema_version <> :schema_uninstalled)', array( + ':theme' => 'theme', + ':module' => 'module', + ':schema_uninstalled' => SCHEMA_UNINSTALLED, + )); + foreach ($result as $file) { + if ($file->status) { + $configs[$file->type]->set($file->name, $file->type == 'module' ? $file->weight : 1); + } + $data = new stdClass(); + $data->info = unserialize($file->info); + foreach ($keys[$file->type] as $key) { + $data->$key = $file->$key; + } + if (preg_match('#^'. $file->type . 's/#', $data->filename)) { + $data->filename = 'core/' . $data->filename; + } + $stores[$file->type]->set($file->name, $data); + } + foreach ($configs as $config) { + $config->save(); + } + + // Update the dynamic include paths that might be used before running the + // proper update functions. + update_prepare_stored_includes(); + // Update the environment for the language bootstrap if needed. + update_prepare_d8_language(); + // Prime the classloader. + system_list('module_enabled'); // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { @@ -318,20 +391,15 @@ function update_module_add_to_system($modules = array()) { 'files' => array(), 'bootstrap' => 0, ); + $store = KeyValueFactory::get('system.module'); foreach ($modules as $module) { $module_info = drupal_parse_info_file('core/modules/' . $module . '/' . $module . '.info'); - db_insert('system') - ->fields(array( - 'filename' => 'core/modules/' . $module . '/' . $module . '.module', - 'name' => $module, - 'type' => 'module', - 'status' => 0, - 'bootstrap' => 0, - 'schema_version' => -1, - 'weight' => 0, - 'info' => serialize($module_info + $info_defaults), - )) - ->execute(); + $store->set($module, (object) array( + 'filename' => 'core/modules/' . $module . '/' . $module . '.module', + 'bootstrap' => 0, + 'schema_version' => SCHEMA_UNINSTALLED, + 'info' => $module_info + $info_defaults, + )); } } @@ -357,6 +425,7 @@ function update_fix_d8_requirements() { * Helper function to install a new module in Drupal 8 via hook_update_N(). */ function update_module_enable(array $modules) { + $store = KeyValueFactory::get('system.module'); foreach ($modules as $module) { // Check for initial schema and install it. The schema version of a newly // installed module is always 0. Using 8000 here would be inconsistent @@ -369,13 +438,13 @@ function update_module_enable(array $modules) { db_create_table($table, $spec); } } + // Enable the module with a weight of 0. + module_config()->set($module, 0)->save(); // Change the schema version from SCHEMA_UNINSTALLED to 0, so any module // updates since the module's inception are executed in a core upgrade. - db_update('system') - ->condition('type', 'module') - ->condition('name', $module) - ->fields(array('schema_version' => 0, 'status' => 1)) - ->execute(); + $data = $store->get($module); + $data->schema_version = 0; + $store->set($module, $data); // system_list_reset() is in module.inc but that would only be available // once the variable bootstrap is done. @@ -910,8 +979,7 @@ function update_retrieve_dependencies() { $return = array(); // Get a list of installed modules, arranged so that we invoke their hooks in // the same order that module_invoke_all() does. - $modules = db_query("SELECT name FROM {system} WHERE type = 'module' AND schema_version <> :schema ORDER BY weight ASC, name ASC", array(':schema' => SCHEMA_UNINSTALLED))->fetchCol(); - foreach ($modules as $module) { + foreach (config('system.module')->get() as $module => $data) { $function = $module . '_update_dependencies'; if (function_exists($function)) { $result = $function(); diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 538ab6d..adf74fa 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -64,6 +64,9 @@ class Config { */ protected $eventDispatcher; + + protected $sorted = FALSE; + /** * Constructs a configuration object. * @@ -310,13 +313,19 @@ class Config { * Saves the configuration object. */ public function save() { - $this->sortByKey($this->data); + if (!$this->sorted) { + $this->sortByKey($this->data); + } $this->storage->write($this->name, $this->data); $this->isNew = FALSE; $this->notify('save'); return $this; } + public function sorted() { + $this->sorted = TRUE; + } + /* * Renames the configuration object. * diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php index ca36ce7..318deda 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactory.php +++ b/core/lib/Drupal/Core/Config/ConfigFactory.php @@ -57,11 +57,13 @@ class ConfigFactory { * * @param string $name * The name of the configuration object to construct. + * @param $class_name + * Optional. The name of the class handling this configuration object. * * @return Drupal\Core\Config\Config * A configuration object with the given $name. */ - public function get($name) { + public function get($name, $class_name = 'Drupal\Core\Config\Config') { global $conf; // @todo Caching the instantiated objects per name might cut off a fair @@ -82,7 +84,7 @@ class ConfigFactory { // @todo The decrease of CPU time is interesting, since that means that // ContainerBuilder involves plenty of function calls (which are known to // be slow in PHP). - $config = new Config($name, $this->storage, $this->eventDispatcher); + $config = new $class_name($name, $this->storage, $this->eventDispatcher); return $config->init(); } diff --git a/core/lib/Drupal/Core/Config/ModuleConfig.php b/core/lib/Drupal/Core/Config/ModuleConfig.php new file mode 100644 index 0000000..a2f6396 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ModuleConfig.php @@ -0,0 +1,34 @@ +data) { + $sort = array(); + foreach ($this->data as $name => $weight) { + // We can't use the sign directly because + (ASCII 43) is before + // - (ASCII 45). So negative nubmers get a 0, non-negative numbers + // a 1 prefix. + $prefix = (int) ($weight >= 0); + // PHP_INT_MAX is at most 19 characters so make every number equally + // 19 digits long. + $sort[] = $prefix . sprintf('%019d', abs($weight)) . $name; + } + array_multisort($sort, SORT_STRING, $this->data); + } + $this->storage->write($this->name, $this->data); + $this->isNew = FALSE; + $this->notify('save'); + return $this; + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php b/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php new file mode 100644 index 0000000..408b9a8 --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php @@ -0,0 +1,54 @@ +collection = $collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::get(). + */ + public function get($key) { + $values = $this->getMultiple(array($key)); + return reset($values); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + foreach ($data as $key => $value) { + $this->set($key, $value); + } + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + $this->deleteMultiple(array($key)); + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php new file mode 100644 index 0000000..de49f78 --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php @@ -0,0 +1,89 @@ +table = isset($options['table']) ? $options['table'] : 'keyvalue'; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + try { + $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name'); + $values = array(); + foreach ($keys as $key) { + if (isset($result[$key]) && ($value = unserialize($result[$key]->value))) { + $values[$key] = $value; + } + } + return $values; + } + catch (\Exception $e) { + // If the database is never going to be available, key/value requests should + // return FALSE in order to allow exception handling to occur. + return array(); + } + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getAll(). + */ + public function getAll() { + $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection)); + $values = array(); + + foreach ($result as $item) { + if ($item) { + $values[$item->name] = unserialize($item->value); + } + } + return $values; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + db_merge($this->table) + ->key(array('name' => $key)) + ->fields(array( + 'collection' => $this->collection, + 'value' => serialize($value), + )) + ->execute(); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + // Delete in chunks when a large array is passed. + do { + db_delete($this->table) + ->condition('name', array_splice($keys, 0, 1000)) + ->condition('collection', $this->collection) + ->execute(); + } + while (count($keys)); + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php new file mode 100644 index 0000000..9536c97 --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php @@ -0,0 +1,18 @@ +collection = $collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::get(). + */ + public function get($key) { + return isset($this->data[$key]) ? $this->data[$key] : FALSE; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + $results = array(); + // Foreach only needs to traverse $keys once, the PHP implementation + // array_intersect_key(array_flip(array_values($keys)), $this->data); + // would be slower. + foreach ($keys as $key) { + if (isset($this->data[$key])) { + $results[$key] = $this->data[$key]; + } + } + return $results; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getAll(). + */ + public function getAll() { + return $this->data; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + $this->data[$key] = $value; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + $this->data = $data + $this->data; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + unset($this->data[$key]); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + foreach ($keys as $key) { + unset($this->data[$key]); + } + } + +} diff --git a/core/modules/block/block.install b/core/modules/block/block.install index 4c5149b..36854ea 100644 --- a/core/modules/block/block.install +++ b/core/modules/block/block.install @@ -213,14 +213,10 @@ function block_schema() { * Implements hook_install(). */ function block_install() { - // Block should go first so that other modules can alter its output // during hook_page_alter(). Almost everything on the page is a block, // so before block module runs, there will not be much to alter. - db_update('system') - ->fields(array('weight' => -5)) - ->condition('name', 'block') - ->execute(); + module_set_weight('block', -5); } /** diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 0626c08..7b67116 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -961,9 +961,8 @@ function block_rebuild() { // Rehash blocks for active themes. We don't use list_themes() here, // because if MAINTENANCE_MODE is defined it skips reading the database, // and we can't tell which themes are active. - $themes = db_query("SELECT name FROM {system} WHERE type = 'theme' AND status = 1"); - foreach ($themes as $theme) { - _block_rehash($theme->name); + foreach (config('system.theme')->get() as $name => $weight) { + _block_rehash($name); } } diff --git a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php index 8902d4c..0c8c2a2 100644 --- a/core/modules/color/lib/Drupal/color/Tests/ColorTest.php +++ b/core/modules/color/lib/Drupal/color/Tests/ColorTest.php @@ -108,7 +108,7 @@ class ColorTest extends WebTestBase { $config->set('preprocess.css', 1); $config->save(); $this->drupalGet(''); - $stylesheets = variable_get('drupal_css_cache_files', array()); + $stylesheets = state()->get('drupal_css_cache_files') ?: array(); $stylesheet_content = ''; foreach ($stylesheets as $key => $uri) { $stylesheet_content .= join("\n", file(drupal_realpath($uri))); diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 73c4a83..5bff879 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -1553,7 +1553,7 @@ function hook_field_available_languages_alter(&$langcodes, $context) { function hook_field_attach_create_bundle($entity_type, $bundle) { // When a new bundle is created, the menu needs to be rebuilt to add the // Field UI menu item tabs. - variable_set('menu_rebuild_needed', TRUE); + state()->set('menu_rebuild_needed', TRUE); } /** diff --git a/core/modules/field/tests/modules/field_test/field_test.install b/core/modules/field/tests/modules/field_test/field_test.install index 5957561..5355e9b 100644 --- a/core/modules/field/tests/modules/field_test/field_test.install +++ b/core/modules/field/tests/modules/field_test/field_test.install @@ -10,10 +10,7 @@ */ function field_test_install() { // hook_entity_info_alter() needs to be executed as last. - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'field_test') - ->execute(); + module_set_weight('field_test', 1); } /** diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index bde3f35..63cf18b 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -309,7 +309,7 @@ function field_ui_element_info() { function field_ui_field_attach_create_bundle($entity_type, $bundle) { // When a new bundle is created, the menu needs to be rebuilt to add our // menu item tabs. - variable_set('menu_rebuild_needed', TRUE); + state()->set('menu_rebuild_needed', TRUE); } /** diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 8e6fddf..56f8402 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -10,10 +10,7 @@ */ function forum_install() { // Set the weight of the forum.module to 1 so it is loaded after the taxonomy.module. - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'forum') - ->execute(); + module_set_weight('forum', 1); // Forum topics are published by default, but do not have any other default // options set (for example, they are not promoted to the front page). // @todo Convert to default module configuration, once Node module's content diff --git a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php index 8404d85..d823187 100644 --- a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php +++ b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php @@ -8,6 +8,7 @@ namespace Drupal\help\Tests; use Drupal\simpletest\WebTestBase; +use Drupal\Core\KeyValueStore\KeyValueFactory; /** * Tests help display and user access for all modules implementing help. @@ -107,11 +108,11 @@ class HelpTest extends WebTestBase { */ protected function getModuleList() { $modules = array(); - $result = db_query("SELECT name, filename, info FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC"); - foreach ($result as $module) { - if (file_exists($module->filename) && function_exists($module->name . '_help')) { - $fullname = unserialize($module->info); - $modules[$module->name] = $fullname['name']; + $enabled_modules = module_config()->get(); + $module_data = KeyValueFactory::get('system.module')->getMultiple(array_keys($enabled_modules)); + foreach ($module_data as $module => $data) { + if (file_exists($data->filename) && function_exists($module . '_help')) { + $modules[$module] = $data->info['name']; } } return $modules; diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 775de2d..7516006 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -253,7 +253,7 @@ function image_form_system_file_system_settings_alter(&$form, &$form_state) { */ function image_system_file_system_settings_submit($form, &$form_state) { if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) { - variable_set('menu_rebuild_needed', TRUE); + state()->set('menu_rebuild_needed', TRUE); } } diff --git a/core/modules/search/search.admin.inc b/core/modules/search/search.admin.inc index 9168510..57e5d6b 100644 --- a/core/modules/search/search.admin.inc +++ b/core/modules/search/search.admin.inc @@ -175,7 +175,7 @@ function search_admin_settings_submit($form, &$form_state) { if ($config->get('active_modules') != $new_modules) { $config->set('active_modules', $new_modules); drupal_set_message(t('The active search modules have been changed.')); - variable_set('menu_rebuild_needed', TRUE); + state()->set('menu_rebuild_needed', TRUE); } $config->save(); } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 10501d0..b7bff5a 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -661,12 +661,7 @@ abstract class WebTestBase extends TestBase { } // Run the profile tasks. - $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( - ':name' => $this->profile, - ))->fetchField(); - if ($install_profile_module_exists) { - module_enable(array($this->profile), FALSE); - } + module_enable(array($this->profile), FALSE); // Create a new DrupalKernel for testing purposes, now that all required // modules have been enabled. This also stores a new dependency injection @@ -1029,12 +1024,14 @@ abstract class WebTestBase extends TestBase { * The retrieved HTML string, also available as $this->drupalGetContent() */ protected function drupalGet($path, array $options = array(), array $headers = array()) { - $options['absolute'] = TRUE; - // We re-using a CURL connection here. If that connection still has certain // options set, it might change the GET into a POST. Make sure we clear out // previous options. - $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); + if (!url_is_external($path) || $options) { + $options['absolute'] = TRUE; + $path = url($path, $options); + } + $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $path, CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. // Replace original page output with new output from redirected page(s). diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index f216339..49b1e9f 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -1,6 +1,7 @@ fetchAllKeyed(); - foreach ($system_list as $name => $filename) { + foreach ($module_data as $name => $data) { + $dirname = dirname($data->filename); // Build directory in which the test files would reside. - $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $name . '/Tests'; + $tests_dir = DRUPAL_ROOT . '/' . $dirname . '/lib/Drupal/' . $name . '/Tests'; // Scan it for test files if it exists. if (is_dir($tests_dir)) { $files = file_scan_directory($tests_dir, '/.*\.php/'); if (!empty($files)) { - $basedir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/'; + $basedir = DRUPAL_ROOT . '/' . $dirname . '/lib/'; foreach ($files as $file) { // Convert the file name into the namespaced class name. $replacements = array( @@ -357,7 +359,7 @@ function simpletest_test_get_all() { // If this test class requires a non-existing module, skip it. if (!empty($info['dependencies'])) { foreach ($info['dependencies'] as $module) { - if (!drupal_get_filename('module', $module)) { + if (!isset($module_data[$module])) { continue 2; } } @@ -384,12 +386,13 @@ function simpletest_test_get_all() { /** * Registers namespaces for disabled modules. */ -function simpletest_classloader_register() { - // Get the cached test modules list and register a test namespace for each. - $disabled_modules = db_query("SELECT name, filename FROM {system} WHERE status = 0")->fetchAllKeyed(); - if ($disabled_modules) { - foreach ($disabled_modules as $name => $filename) { - drupal_classloader_register($name, dirname($filename)); +function simpletest_classloader_register($module_data = NULL) { + if (!$module_data) { + $module_data = system_rebuild_module_data(); + } + foreach ($module_data as $name => $data) { + if (!$data->status) { + drupal_classloader_register($name, dirname($data->filename)); } } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Actions/LoopTest.php b/core/modules/system/lib/Drupal/system/Tests/Actions/LoopTest.php index 86d99f9..8bfd9bb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Actions/LoopTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Actions/LoopTest.php @@ -67,7 +67,6 @@ class LoopTest extends WebTestBase { $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.'; $result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid"); - $loop_started = FALSE; foreach ($result as $row) { $expected_message = array_shift($expected); $this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message))); diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php index ba7a48b..f72d384 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php @@ -346,11 +346,11 @@ class JavaScriptTest extends WebTestBase { )); // Store the expected key for the first item in the cache. - $cache = array_keys(variable_get('drupal_js_cache_files', array())); + $cache = array_keys(state()->get('drupal_js_cache_files') ?: array()); $expected_key = $cache[0]; // Reset variables and add a file in a different scope first. - variable_del('drupal_js_cache_files'); + state()->delete('drupal_js_cache_files'); drupal_static_reset('drupal_add_js'); drupal_add_library('system', 'drupal'); drupal_add_js('some/custom/javascript_file.js', array('scope' => 'footer')); @@ -365,7 +365,7 @@ class JavaScriptTest extends WebTestBase { )); // Compare the expected key for the first file to the current one. - $cache = array_keys(variable_get('drupal_js_cache_files', array())); + $cache = array_keys(state()->get('drupal_js_cache_files') ?: array()); $key = $cache[0]; $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php index 91d6c97..b92e5ce 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php @@ -34,11 +34,11 @@ class RangeQueryTest extends WebTestBase { */ function testRangeQuery() { // Test if return correct number of rows. - $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll(); + $range_rows = db_query_range("SELECT name FROM {variable} ORDER BY name", 2, 3)->fetchAll(); $this->assertEqual(count($range_rows), 3, t('Range query work and return correct number of rows.')); // Test if return target data. - $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll(); + $raw_rows = db_query('SELECT name FROM {variable} ORDER BY name')->fetchAll(); $raw_rows = array_slice($raw_rows, 2, 3); $this->assertEqual($range_rows, $raw_rows, t('Range query work and return target data.')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php index 24a61fa..092376f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php @@ -43,7 +43,7 @@ class TemporaryQueryTest extends WebTestBase { $this->drupalGet('database_test/db_query_temporary'); $data = json_decode($this->drupalGetContent()); if ($data) { - $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.')); + $this->assertEqual($this->countTableRows("filter"), $data->row_count, t('The temporary table contains the correct amount of rows.')); $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.')); } else { @@ -51,10 +51,10 @@ class TemporaryQueryTest extends WebTestBase { } // Now try to run two db_query_temporary() in the same request. - $table_name_system = db_query_temporary('SELECT status FROM {system}', array()); + $table_name_system = db_query_temporary('SELECT name FROM {variable}', array()); $table_name_users = db_query_temporary('SELECT uid FROM {users}', array()); - $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.')); + $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("variable"), t('A temporary table was created successfully in this request.')); $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.')); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php new file mode 100644 index 0000000..c81f8e9 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php @@ -0,0 +1,26 @@ + 'Database Storage', + 'description' => 'Test the key-value database storage.', + 'group' => 'Key-value store' + ); + } + + protected function setUp() { + parent::setUp(); + module_load_install('system'); + $schema = system_schema(); + db_create_table('keyvalue', $schema['keyvalue']); + } + + protected function tearDown() { + db_drop_table('keyvalue'); + parent::tearDown(); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php new file mode 100644 index 0000000..d3db3bc --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php @@ -0,0 +1,15 @@ + 'Memory Storage', + 'description' => 'Test the key-value memory storage.', + 'group' => 'Key-value store' + ); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php new file mode 100644 index 0000000..8fb38a0 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php @@ -0,0 +1,41 @@ +collection = end($class); + $storageClass = '\\Drupal\\Core\\KeyValueStore\\' . substr($this->collection, 0, -4); + $this->kv = new $storageClass($this->collection); + } + + public function testSetGet() { + $this->assertEqual($this->kv->getCollectionName(), $this->collection); + // get-set test. + $this->kv->set('foo', 'bar'); + $this->assertEqual('bar', $this->kv->get('foo')); + $this->kv->delete('foo'); + $this->assertFalse($this->kv->get('foo')); + + // multiple test. + $values = array('foo' => 'bar', 'baz' => 'qux'); + $this->kv->setMultiple($values); + $result = $this->kv->getMultiple(array('foo', 'baz')); + $this->assertEqual($values, $result); + $result = $this->kv->getAll(); + // $result might not equal to $values, the order is not defined for + // getAll(). + $this->assertEqual(count($result), count($values)); + foreach ($result as $key => $value) { + $this->assertEqual($values[$key], $value); + } + $this->kv->deleteMultiple(array_keys($values)); + $this->assertFalse($this->kv->get('foo')); + $this->assertFalse($this->kv->get('bar')); + $this->assertFalse($this->kv->getMultiple(array('foo', 'baz'))); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/RebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/RebuildTest.php index b6e0ec2..9cf1080 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/RebuildTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/RebuildTest.php @@ -38,7 +38,7 @@ class RebuildTest extends WebTestBase { // Now we enable the rebuild variable and send a request to rebuild the menu // item. Now 'admin' should exist. - variable_set('menu_rebuild_needed', TRUE); + state()->set('menu_rebuild_needed', TRUE); // The request should trigger the rebuild. $this->drupalGet(''); $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php index 8468185..9aaf6d5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -49,11 +49,7 @@ class ModuleApiTest extends WebTestBase { $this->assertModuleList($module_list, t('After adding a module')); // Try to mess with the module weights. - db_update('system') - ->fields(array('weight' => 20)) - ->condition('name', 'contact') - ->condition('type', 'module') - ->execute(); + module_set_weight('contact', 20); // Reset the module list. system_list_reset(); // Move contact to the end of the array. diff --git a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php index 9bdeac4..3ec9b22 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\System; use Drupal\simpletest\WebTestBase; +use Drupal\Core\KeyValueStore\KeyValueFactory; /** * Tests the effectiveness of hook_system_info_alter(). @@ -71,7 +72,9 @@ class InfoAlterTest extends WebTestBase { * Array of info, or FALSE if the record is not found. */ function getSystemInfo($name, $type) { - $raw_info = db_query("SELECT info FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField(); - return $raw_info ? unserialize($raw_info) : FALSE; + if (($data = KeyValueFactory::get("system.$type")->get($name)) && $data->info) { + return $data->info; + } + return FALSE; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php index 1231af4..918eb96 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php @@ -129,10 +129,10 @@ class UpdateScriptTest extends WebTestBase { // Since visiting update.php triggers a rebuild of the theme system from an // unusual maintenance mode environment, we check that this rebuild did not // put any incorrect information about the themes into the database. - $original_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll(); + $original_theme_data = config('system.theme')->get(); $this->drupalLogin($this->update_user); $this->drupalGet($this->update_url, array('external' => TRUE)); - $final_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll(); + $final_theme_data = config('system.theme')->get(); $this->assertEqual($original_theme_data, $final_theme_data, t('Visiting update.php does not alter the information about themes stored in the database.')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php index a0f6c59..7ad0b31 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -78,6 +78,8 @@ abstract class UpgradePathTestBase extends WebTestBase { // Load the Update API. require_once DRUPAL_ROOT . '/core/includes/update.inc'; + // Disable verbose because it calls url() too early. + $this->verbose = FALSE; // Reset flags. $this->upgradedSite = FALSE; $this->upgradeErrors = array(); @@ -189,7 +191,7 @@ abstract class UpgradePathTestBase extends WebTestBase { // Load the first update screen. $update_url = $GLOBALS['base_url'] . '/core/update.php'; - $this->drupalGet($update_url, array('external' => TRUE)); + $this->drupalGet($update_url); if (!$this->assertResponse(200)) { return FALSE; } @@ -230,7 +232,7 @@ abstract class UpgradePathTestBase extends WebTestBase { } // Check if there still are pending updates. - $this->drupalGet($update_url, array('external' => TRUE)); + $this->drupalGet($update_url); $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { return FALSE; @@ -273,10 +275,13 @@ abstract class UpgradePathTestBase extends WebTestBase { $modules = array_merge($required_modules, $modules); - db_delete('system') - ->condition('type', 'module') - ->condition('name', $modules, 'NOT IN') - ->execute(); + $config = config('system.module'); + foreach ($config->get() as $module => $data) { + if (!in_array($module, $required_modules)) { + $config->clear($module); + } + } + $config->save(); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 484b384..fc91872 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -509,11 +509,7 @@ function system_requirements($phase) { function system_install() { // Enable the default theme. variable_set('theme_default', 'stark'); - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'stark') - ->execute(); + config('system.theme')->set('stark', 1)->save(); // Populate the cron key variable. $cron_key = drupal_hash_base64(drupal_random_bytes(55)); @@ -549,6 +545,60 @@ function system_schema() { ), 'primary key' => array('name'), ); + $schema['keyvalue'] = array( + 'description' => 'Generic key-value storage table.', + 'fields' => array( + 'name' => array( + 'description' => 'The key.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'collection' => array( + 'description' => 'The collection of the variable.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value.', + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'big', + 'translatable' => TRUE, + ), + ), + 'primary key' => array('collection', 'name'), + ); + $schema['keyvalue'] = array( + 'description' => 'Generic key-value storage table.', + 'fields' => array( + 'name' => array( + 'description' => 'The key.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'collection' => array( + 'description' => 'The collection of the variable.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value.', + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'big', + 'translatable' => TRUE, + ), + ), + 'primary key' => array('collection', 'name'), + ); $schema['actions'] = array( 'description' => 'Stores action information.', @@ -1362,75 +1412,6 @@ function system_schema() { ), ); - $schema['system'] = array( - 'description' => "A list of all modules, themes, and theme engines that are or have been installed in Drupal's file system.", - 'fields' => array( - 'filename' => array( - 'description' => 'The path of the primary file for this item, relative to the Drupal root; e.g. modules/node/node.module.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The name of the item; e.g. node.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'type' => array( - 'description' => 'The type of the item, either module, theme, or theme_engine.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'owner' => array( - 'description' => "A theme's 'parent' . Can be either a theme or an engine.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'status' => array( - 'description' => 'Boolean indicating whether or not this item is enabled.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'bootstrap' => array( - 'description' => "Boolean indicating whether this module is loaded during Drupal's early bootstrapping phase (e.g. even before the page cache is consulted).", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'schema_version' => array( - 'description' => "The module's database schema version number. -1 if the module is not installed (its tables do not exist); 0 or the largest N of the module's hook_update_N() function that has either been run or existed when the module was first installed.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => -1, - 'size' => 'small', - ), - 'weight' => array( - 'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'info' => array( - 'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.", - 'type' => 'blob', - 'not null' => FALSE, - ), - ), - 'primary key' => array('filename'), - 'indexes' => array( - 'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'), - 'type_name' => array('type', 'name'), - ), - ); - $schema['url_alias'] = array( 'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.', 'fields' => array( diff --git a/core/modules/system/system.module b/core/modules/system/system.module index e5dc768..3c926b2 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Utility\ModuleInfo; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -2609,13 +2610,15 @@ function system_check_directory($form_element) { */ function system_get_files_database(&$files, $type) { // Extract current files from database. - $result = db_query("SELECT filename, name, type, status, schema_version, weight FROM {system} WHERE type = :type", array(':type' => $type)); - foreach ($result as $file) { - if (isset($files[$file->name]) && is_object($files[$file->name])) { + $config = config("system.$type")->get(); + $store = KeyValueFactory::get('system.module'); + foreach ($store->getAll() as $name => $file) { + if (isset($files[$name]) && is_object($files[$name])) { $file->uri = $file->filename; + $file->status = isset($config[$name]); foreach ($file as $key => $value) { - if (!isset($files[$file->name]->$key)) { - $files[$file->name]->$key = $value; + if (!isset($files[$name]->$key)) { + $files[$name]->$key = $value; } } } @@ -2631,77 +2634,60 @@ function system_get_files_database(&$files, $type) { * The type of the files. */ function system_update_files_database(&$files, $type) { - $result = db_query("SELECT * FROM {system} WHERE type = :type", array(':type' => $type)); - - // Add all files that need to be deleted to a DatabaseCondition. - $delete = db_or(); - foreach ($result as $file) { - if (isset($files[$file->name]) && is_object($files[$file->name])) { - // Keep the old filename from the database in case the file has moved. - $old_filename = $file->filename; - - $updated_fields = array(); + $store = KeyValueFactory::get("system.$type"); + foreach ($store->getAll() as $name => $data) { + $update = FALSE; + if (isset($files[$name]) && is_object($files[$name])) { // Handle info specially, compare the serialized value. - $serialized_info = serialize($files[$file->name]->info); - if ($serialized_info != $file->info) { - $updated_fields['info'] = $serialized_info; + $serialized_info = serialize($files[$name]->info); + if ($serialized_info != serialize($data->info)) { + $update = TRUE; + $data->info = $files[$name]->info; } - unset($file->info); // Scan remaining fields to find only the updated values. - foreach ($file as $key => $value) { - if (isset($files[$file->name]->$key) && $files[$file->name]->$key != $value) { - $updated_fields[$key] = $files[$file->name]->$key; + foreach ($data as $key => $value) { + if ($key != 'info' && isset($files[$name]->$key) && $files[$name]->$key != $value) { + $update = TRUE; + $data->$key = $files[$name]->$key; } } // Update the record. - if (count($updated_fields)) { - db_update('system') - ->fields($updated_fields) - ->condition('filename', $old_filename) - ->execute(); + if ($update) { + $store->set($name, $data); } // Indicate that the file exists already. - $files[$file->name]->exists = TRUE; + $files[$name]->exists = TRUE; } - else { - // File is not found in file system, so delete record from the system table. - $delete->condition('filename', $file->filename); + elseif ($type == 'module' && $data->schema_version == SCHEMA_UNINSTALLED) { + $store->delete($name); } } - if (count($delete) > 0) { - // Delete all missing files from the system table, but only if the plugin - // has never been installed. - db_delete('system') - ->condition($delete) - ->condition('schema_version', -1) - ->execute(); - } - // All remaining files are not in the system table, so we need to add them. - $query = db_insert('system')->fields(array('filename', 'name', 'type', 'owner', 'info')); + $config = config("system.$type")->get(); foreach ($files as &$file) { + $file->status = isset($config[$file->name]); if (isset($file->exists)) { unset($file->exists); } else { - $query->values(array( - 'filename' => $file->uri, - 'name' => $file->name, - 'type' => $type, - 'owner' => isset($file->owner) ? $file->owner : '', - 'info' => serialize($file->info), - )); + if ($type == 'theme') { + $data = (object) array( + 'filename' => $file->uri, + 'owner' => isset($file->owner) ? $file->owner : '', + 'info' => $file->info, + ); + $store->set($file->name, $data); + } $file->type = $type; $file->status = 0; $file->schema_version = -1; } } - $query->execute(); // If any module or theme was moved to a new location, we need to reset the // system_list() cache or we will continue to load the old copy, look for @@ -2734,9 +2720,9 @@ function system_update_files_database(&$files, $type) { function system_get_info($type, $name = NULL) { $info = array(); if ($type == 'module') { - $result = db_query('SELECT name, info FROM {system} WHERE type = :type AND status = 1', array(':type' => 'module')); - foreach ($result as $record) { - $info[$record->name] = unserialize($record->info); + $module_data = KeyValueFactory::get('system.module')->getMultiple(array_keys(config('system.module')->get())); + foreach ($module_data as $module => $data) { + $info[$module] = $data->info; } } else { @@ -2891,21 +2877,14 @@ function system_rebuild_module_data() { * are loaded earlier to invoke the hooks. */ function _system_update_bootstrap_status() { - $bootstrap_modules = array(); + $store = KeyValueFactory::get('system.module'); foreach (bootstrap_hooks() as $hook) { foreach (module_implements($hook) as $module) { - $bootstrap_modules[] = $module; + $module_data = $store->get($module); + $module_data->bootstrap = 1; + $store->set($module, $module_data); } } - $query = db_update('system')->fields(array('bootstrap' => 0)); - if ($bootstrap_modules) { - db_update('system') - ->fields(array('bootstrap' => 1)) - ->condition('name', $bootstrap_modules, 'IN') - ->execute(); - $query->condition('name', $bootstrap_modules, 'NOT IN'); - } - $query->execute(); // Reset the cached list of bootstrap modules. system_list_reset(); } @@ -3072,8 +3051,9 @@ function _system_info_add_path($info, $path) { // Unset the original value's key and set the new value with prefix, using // the original value as key, so original values can still be looked up. else { - unset($info[$key]); - $info[$value] = $path . '/' . $value; + // @TODO do something here to not mess up array order! + #unset($info[$key]); + $info[$key] = $path . '/' . $value; } } return $info; diff --git a/core/modules/system/tests/modules/actions_loop_test/actions_loop_test.install b/core/modules/system/tests/modules/actions_loop_test/actions_loop_test.install index b22fd85..8046353 100644 --- a/core/modules/system/tests/modules/actions_loop_test/actions_loop_test.install +++ b/core/modules/system/tests/modules/actions_loop_test/actions_loop_test.install @@ -4,8 +4,5 @@ * Implements hook_install(). */ function actions_loop_test_install() { - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'actions_loop_test') - ->execute(); + module_set_weight('actions_loop_test', 1); } diff --git a/core/modules/system/tests/modules/database_test/database_test.module b/core/modules/system/tests/modules/database_test/database_test.module index 0180b03..93135c9 100644 --- a/core/modules/system/tests/modules/database_test/database_test.module +++ b/core/modules/system/tests/modules/database_test/database_test.module @@ -84,7 +84,7 @@ function database_test_menu() { * table should automatically dropped. */ function database_test_db_query_temporary() { - $table_name = db_query_temporary('SELECT status FROM {system}', array()); + $table_name = db_query_temporary('SELECT status FROM {filter}', array()); return new JsonResponse(array( 'table_name' => $table_name, 'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(), diff --git a/core/modules/system/tests/modules/url_alter_test/url_alter_test.install b/core/modules/system/tests/modules/url_alter_test/url_alter_test.install index 6e09ab5..eb4864f 100644 --- a/core/modules/system/tests/modules/url_alter_test/url_alter_test.install +++ b/core/modules/system/tests/modules/url_alter_test/url_alter_test.install @@ -5,8 +5,5 @@ */ function url_alter_test_install() { // Set the weight of this module to one higher than forum.module. - db_update('system') - ->fields(array('weight' => 2)) - ->condition('name', 'url_alter_test') - ->execute(); + module_set_weight('url_alter_test', 2); } diff --git a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php index 27328c0..92f4fe8 100644 --- a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php +++ b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php @@ -166,11 +166,7 @@ class UpdateContribTest extends UpdateTestBase { */ function testUpdateBaseThemeSecurityUpdate() { // Only enable the subtheme, not the base theme. - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'update_test_subtheme') - ->execute(); + theme_enable(array('update_test_subtheme')); // Define the initial state for core and the subtheme. $system_info = array( @@ -208,11 +204,13 @@ class UpdateContribTest extends UpdateTestBase { function testUpdateShowDisabledThemes() { $update_settings = config('update.settings'); // Make sure all the update_test_* themes are disabled. - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->condition('name', 'update_test_%', 'LIKE') - ->execute(); + $config = config('system.theme'); + foreach ($config->get() as $theme => $weight) { + if (preg_match('/^update_test_/', $theme)) { + $config->clear($theme); + } + } + $config->save(); // Define the initial state for core and the test contrib themes. $system_info = array( diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index f3eaf29..d2385b7 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -419,11 +419,7 @@ function standard_install() { menu_router_rebuild(); // Enable the admin theme. - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'seven') - ->execute(); + config('system.theme')->set('seven', 1)->save(); variable_set('admin_theme', 'seven'); variable_set('node_admin_theme', '1'); }