=== modified file 'includes/common.inc' --- includes/common.inc 2008-01-30 23:07:41 +0000 +++ includes/common.inc 2008-02-15 03:36:41 +0000 @@ -1463,6 +1463,8 @@ function drupal_page_footer() { if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) { page_set_cache(); } + // Store the used registry files. + _registry_mark_file(NULL, TRUE); module_invoke_all('exit'); } @@ -2451,6 +2453,9 @@ function _drupal_bootstrap_full() { unicode_check(); // Undo magic quotes fix_gpc_magic(); + // Register autoload functions so that we can acess classes and interfaces. + spl_autoload_register('registry_check_class'); + spl_autoload_register('registry_check_interface'); // Load all enabled modules module_load_all(); // Let all modules take action before menu system handles the request @@ -2708,7 +2713,7 @@ function drupal_render(&$elements) { // element is rendered into the final text. if (isset($elements['#pre_render'])) { foreach ($elements['#pre_render'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $elements = $function($elements); } } @@ -2770,7 +2775,7 @@ function drupal_render(&$elements) { // which allows the output'ed text to be filtered. if (isset($elements['#post_render'])) { foreach ($elements['#post_render'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $content = $function($content, $elements); } } @@ -3524,6 +3529,7 @@ function drupal_flush_all_caches() { // Change query-strings on css/js files to enforce reload for all users. _drupal_flush_css_js(); + drupal_rebuild_code_registry(); drupal_clear_css_cache(); drupal_clear_js_cache(); drupal_rebuild_theme_registry(); @@ -3531,7 +3537,7 @@ function drupal_flush_all_caches() { node_types_rebuild(); // Don't clear cache_form - in-progress form submissions may break. // Ordered so clearing the page cache will always be the last action. - $core = array('cache', 'cache_block', 'cache_filter', 'cache_page'); + $core = array('cache', 'cache_block', 'cache_filter', 'cache_registry', 'cache_page'); $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); foreach ($cache_tables as $table) { cache_clear_all('*', $table, TRUE); @@ -3556,3 +3562,230 @@ function _drupal_flush_css_js() { } variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19)); } + +/** + * Rescan all installed modules and rebuild the registry. + */ +function drupal_rebuild_code_registry() { + // Flush the old registry. + db_query("DELETE FROM {registry}"); + // We can't use module_invoke_all here because it depends on the registry + // which is being rebuilt right now. + $list = module_list(TRUE, FALSE, FALSE); + $patterns = array(); + foreach ($list as $module) { + $function = $module .'_hooks'; + if (function_exists($function)) { + $result = (array)$function(); + foreach ($result as $pattern) { + // For example '__form_alter' + $patterns[] = '/'. str_replace('__', '.*_', $pattern) .'/'; + } + } + } + + foreach ($list as $module) { + _registry_parse_directory(drupal_get_path('module', $module), $patterns); + } + + _registry_parse_directory('includes', $patterns); + $implementations = _registry_save_resource(); + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); +} + +/** + * Parse all loadable files in a directory and save their function listings. + */ +function _registry_parse_directory($path, $patterns) { + static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'); + $files = file_scan_directory($path, '\.(inc|module)$'); + foreach ($files as $filename => $file) { + $tokens = token_get_all(file_get_contents($filename)); + while ($token = next($tokens)) { + if (is_array($token) && isset($map[$token[0]])) { + _registry_save_resource($token, $tokens, $map[$token[0]], $filename, $patterns); + // We skip the body because classes might contain functions. + _registry_skip_body($tokens); + } + } + } +} + +/** + * Save a resource into the database. + * + * @param mixed $token + * @param ArrayIterator $tokens + * @param string $type + * @param string $module_path + * @param string $filename + */ +function _registry_save_resource($token = NULL, &$tokens = NULL, $type = NULL, $filename = NULL, $patterns = NULL) { + static $implementations, $resources; + if (!isset($token)) { + return $implementations; + } + next($tokens); // Eat a space. + $token = next($tokens); + if ($token == '&') { + $token = next($tokens); + } + $resource_name = $token[1]; + if (isset($resources[$type][$resource_name])) { + return; + } + $resources[$type][$resource_name] = TRUE; + $file_parts = explode('.', $filename); + $module = ''; + $hook = ''; + if (count($file_parts) == 2 || (isset($file_parts[2]) && $file_parts[2] == 'inc')) { + $module = basename($file_parts[0]); + if (strpos($resource_name, $module) === 0) { + $hook = substr($resource_name, strlen($module) + 1); + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + $implementations[$hook][] = $module; + } + } + } + } + db_query("INSERT INTO {registry} (name, type, module, hook, file) VALUES ('%s', '%s', '%s', '%s', '%s')", array($resource_name, $type, $module, $hook, "./$filename")); +} + +/** + * Skip the body of a code block, as defined by { and }. + * + * This function assumes that the body starts at the next instance + * of { from the current position. + * + * @param ArrayIterator $tokens + */ +function _registry_skip_body(&$tokens) { + $num_braces = 1; + + $token = ''; + // Get to the first open brace. + while ($token != '{' && ($token = next($tokens))); + + // Scan through the rest of the tokens until we reach the matching + // end brace. + while ($num_braces && ($token = next($tokens))) { + if ($token == '{') { + ++$num_braces; + } + elseif ($token == '}') { + --$num_braces; + } + } +} + +/** + * Collect the files included so we can preload them on the next pageload. + * + * The function collects the files as the registry includes them and at the + * end of page request it stores them per router path. + * + * @param $file + * A file or an array of files included. + * @param $store + * If true, then store the collected array of files into the database as + * necessary. + */ +function _registry_mark_file($file = NULL, $store = FALSE) { + static $used_files = array(), $original_files = array(); + +if (isset($file)) { + if (is_array($file)) { + $used_files = array_merge($used_files, $file); + } + else { + $used_files[] = $file; + } + } + elseif ($store && $used_files != $original_files) { + // files we will likely need next time. + $files = array_unique($used_files); + $files = implode(';', $files); + $menu = menu_get_item(); + cache_set($menu['path'], $files, 'cache_registry'); + } +} + +/** + * Confirm that a function is available. + * + * If the function is already available, this function does nothing. + * If it is not, it tries to load the file in which it lives. If + * the file is not available, it simply returns false. That way it + * can be used as a drop-in replacement for function_exists(). + * + * @param string $function + * The name of the function to check or load. + * @return + * TRUE if the function is now available, FALSE otherwise. + */ +function registry_check_function($function) { + static $checked = array(); + if (isset($checked[$function])) { + return $checked[$function]; + } + if (!function_exists($function)) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'function'", $function)); + if (!$file) { + $checked[$function] = FALSE; + return FALSE; + } + //dsm(__FUNCTION__ . ": Including $file"); + if ($file == './includes/theme.maintenance.inc') { + var_export(debug_backtrace()); + } + require_once($file); + _registry_mark_file($file); + } + $checked[$function] = TRUE; + return TRUE; +} + +/** + * Confirm that a class is available. + * + * This function parallel's registry_check_function(), but will rarely + * be called directly. Instead, it will be registered as an spl_autoload() + * handler, and PHP will call it for us when necessary. + * + * @param string $class + * The name of the class to check or load. + * @return + * TRUE if the class is now available, FALSE otherwise. + */ +function registry_check_class($class) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'class'", $class)); + if (!$file) { + return FALSE; + } + require_once($file); + _registry_mark_file($file); + return TRUE; +} + +/** + * Confirm that an interface is available. + * + * This function parallel's registry_check_function(), but will rarely + * be called directly. Instead, it will be registered as an spl_autoload() + * handler, and PHP will call it for us when necessary. + * + * @param string $interface + * The name of the interface to check or load. + * @return + * TRUE if the interface is now available, FALSE otherwise. + */ +function registry_check_interface($interface) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'interface'", $interface)); + if (!$file) { + return FALSE; + } + require_once($file); + _registry_mark_file($file); + return TRUE; +} === modified file 'includes/form.inc' --- includes/form.inc 2008-02-12 13:52:32 +0000 +++ includes/form.inc 2008-02-15 00:31:18 +0000 @@ -326,7 +326,7 @@ function drupal_retrieve_form($form_id, // We first check to see if there's a function named after the $form_id. // If there is, we simply pass the arguments on to it to get the form. - if (!function_exists($form_id)) { + if (!registry_check_function($form_id)) { // In cases where many form_ids need to share a central constructor function, // such as the node editing form, modules can implement hook_forms(). It // maps one or more form_ids to the correct constructor functions. @@ -347,6 +347,7 @@ function drupal_retrieve_form($form_id, } if (isset($form_definition['callback'])) { $callback = $form_definition['callback']; + registry_check_function($callback); } } @@ -503,13 +504,13 @@ function drupal_prepare_form($form_id, & $form += _element_info('form'); if (!isset($form['#validate'])) { - if (function_exists($form_id .'_validate')) { + if (registry_check_function($form_id .'_validate')) { $form['#validate'] = array($form_id .'_validate'); } } if (!isset($form['#submit'])) { - if (function_exists($form_id .'_submit')) { + if (registry_check_function($form_id .'_submit')) { // We set submit here so that it can be altered. $form['#submit'] = array($form_id .'_submit'); } @@ -710,7 +711,7 @@ function _form_validate($elements, &$for // #value data. elseif (isset($elements['#element_validate'])) { foreach ($elements['#element_validate'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $function($elements, $form_state, $complete_form); } } @@ -747,7 +748,7 @@ function form_execute_handlers($type, &$ } foreach ($handlers as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { if ($type == 'submit' && ($batch =& batch_get())) { // Some previous _submit handler has set a batch. We store the call // in a special 'control' batch set, for execution at the correct @@ -1030,7 +1031,7 @@ function _form_builder_handle_input_elem // checkboxes and files. if (isset($form['#process']) && !$form['#processed']) { foreach ($form['#process'] as $process) { - if (function_exists($process)) { + if (registry_check_function($process)) { $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form); } } === modified file 'includes/install.inc' --- includes/install.inc 2008-02-12 13:45:16 +0000 +++ includes/install.inc 2008-02-15 02:40:11 +0000 @@ -687,8 +687,9 @@ function drupal_check_profile($profile) $requirements = array(); foreach ($installs as $install) { require_once $install->filename; - if (module_hook($install->name, 'requirements')) { - $requirements = array_merge($requirements, module_invoke($install->name, 'requirements', 'install')); + $function = $install->name. '_requirements'; + if (function_exists($function)) { + $requirements = array_merge($requirements, $function('install')); } } return $requirements; === modified file 'includes/mail.inc' --- includes/mail.inc 2008-02-06 19:38:26 +0000 +++ includes/mail.inc 2008-02-14 00:43:32 +0000 @@ -115,7 +115,7 @@ function drupal_mail($module, $key, $to, // Build the e-mail (get subject and body, allow additional headers) by // invoking hook_mail() on this module. We cannot use module_invoke() as // we need to have $message by reference in hook_mail(). - if (function_exists($function = $module .'_mail')) { + if (registry_check_function($function = $module .'_mail')) { $function($key, $message, $params); } === modified file 'includes/menu.inc' --- includes/menu.inc 2008-02-10 19:49:37 +0000 +++ includes/menu.inc 2008-02-15 01:11:27 +0000 @@ -340,11 +340,18 @@ function menu_execute_active_handler($pa menu_rebuild(); } if ($router_item = menu_get_item($path)) { + $cache = cache_get($router_item['path'], 'cache_registry'); + if (!empty($cache->data)) { + $files = explode(';', $cache->data); + foreach($files as $file) { + require_once($file); + } + _registry_mark_file($files); + } if ($router_item['access']) { - if ($router_item['file']) { - require_once($router_item['file']); + if (registry_check_function($router_item['page_callback'])) { + return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } - return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } else { return MENU_ACCESS_DENIED; @@ -2169,12 +2176,12 @@ function _menu_router_build($callbacks) $load_functions[$k] = NULL; } else { - if (function_exists($matches[1] .'_to_arg')) { + if (registry_check_function($matches[1] .'_to_arg')) { $to_arg_functions[$k] = $matches[1] .'_to_arg'; $load_functions[$k] = NULL; $match = TRUE; } - if (function_exists($matches[1] .'_load')) { + if (registry_check_function($matches[1] .'_load')) { $function = $matches[1] .'_load'; // Create an array of arguments that will be passed to the _load // function when this menu path is checked, if 'load arguments' @@ -2260,12 +2267,6 @@ function _menu_router_build($callbacks) if (!isset($item['page arguments']) && isset($parent['page arguments'])) { $item['page arguments'] = $parent['page arguments']; } - if (!isset($item['file']) && isset($parent['file'])) { - $item['file'] = $parent['file']; - } - if (!isset($item['file path']) && isset($parent['file path'])) { - $item['file path'] = $parent['file path']; - } } } } @@ -2293,34 +2294,25 @@ function _menu_router_build($callbacks) 'tab_parent' => '', 'tab_root' => $path, 'path' => $path, - 'file' => '', - 'file path' => '', - 'include file' => '', ); - // Calculate out the file to be included for each callback, if any. - if ($item['file']) { - $file_path = $item['file path'] ? $item['file path'] : drupal_get_path('module', $item['module']); - $item['include file'] = $file_path .'/'. $item['file']; - } - $title_arguments = $item['title arguments'] ? serialize($item['title arguments']) : ''; db_query("INSERT INTO {menu_router} (path, load_functions, to_arg_functions, access_callback, access_arguments, page_callback, page_arguments, fit, number_parts, tab_parent, tab_root, title, title_callback, title_arguments, - type, block_callback, description, position, weight, file) + type, block_callback, description, position, weight) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', - %d, '%s', '%s', '%s', %d, '%s')", + %d, '%s', '%s', '%s', %d)", $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'], serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'], $item['_number_parts'], $item['tab_parent'], $item['tab_root'], $item['title'], $item['title callback'], $title_arguments, - $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight'], $item['include file']); + $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight']); } // Sort the masks so they are in order of descending fit, and store them. $masks = array_keys($masks); === modified file 'includes/module.inc' --- includes/module.inc 2007-12-27 12:31:05 +0000 +++ includes/module.inc 2008-02-15 03:35:03 +0000 @@ -228,7 +228,7 @@ function _module_build_dependencies($fil */ function module_exists($module) { $list = module_list(); - return array_key_exists($module, $list); + return isset($list[$module]); } /** @@ -308,7 +308,7 @@ function module_enable($module_list) { // We check for the existence of node_access_needs_rebuild() since // at install time, module_enable() could be called while node.module // is not enabled yet. - if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { + if (registry_check_function('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { node_access_needs_rebuild(TRUE); } } @@ -383,7 +383,9 @@ function module_disable($module_list) { * implemented in that module. */ function module_hook($module, $hook) { - return function_exists($module .'_'. $hook); + $function = $module .'_'. $hook; + // During bootstrap, registry_check_function might not exist. + return function_exists('registry_check_function') ? registry_check_function($function) : function_exists($function); } /** @@ -402,30 +404,58 @@ function module_hook($module, $hook) { * An array with the names of the modules which are implementing this hook. */ function module_implements($hook, $sort = FALSE, $refresh = FALSE) { - static $implementations; + static $implementations, $patterns; if ($refresh) { $implementations = array(); + cache_clear_all('hooks', 'cache'); return; } - - if (!isset($implementations[$hook])) { - $implementations[$hook] = array(); - $list = module_list(FALSE, TRUE, $sort); - foreach ($list as $module) { - if (module_hook($module, $hook)) { - $implementations[$hook][] = $module; + if (empty($implementations)) { + // During install we can not rely on the registry. + if (function_exists('install_verify_settings') && (!install_verify_settings() || !db_set_active() || !install_verify_drupal() || !function_exists('cache_get'))) { + $list = module_list(); + $return = array(); + foreach ($list as $module) { + $function = $module .'_'. $hook; + if (function_exists($function)) { + $return[] = $module; + } } + return $return; + } + $cache = cache_get('hooks'); + if (!$cache) { + drupal_rebuild_code_registry(); + $cache = cache_get('hooks'); } + $implementations = $cache->data['implementations']; + $patterns = $cache->data['patterns']; } - - // The explicit cast forces a copy to be made. This is needed because - // $implementations[$hook] is only a reference to an element of - // $implementations and if there are nested foreaches (due to nested node - // API calls, for example), they would both manipulate the same array's - // references, which causes some modules' hooks not to be called. - // See also http://www.zend.com/zend/art/ref-count.php. - return (array)$implementations[$hook]; + if (isset($implementations[$hook])) { + return (array)$implementations[$hook]; + } + // We do not store empty arrays for every instance of dynamic hooks like + // hook_{$form_id}_form_alter. The actual implementations of dynamic + // hooks are cached by drupal_rebuild_code_registry so those are already + // returned. If the hook matches a pattern here then we know that there is + // no implementation. + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + return array(); + } + } + // Other hooks are fetched from the registry and cached. + $implementations[$hook] = array(); + $result = db_query("SELECT module, name, file FROM {registry} WHERE hook = '%s' AND type = 'function'", $hook); + while ($entry = db_fetch_array($result)) { + if (!function_exists($entry['name'])) { + require_once($entry['file']); + } + $implementations[$hook][] = $entry['module']; + } + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); + return $implementations[$hook]; } /** === modified file 'includes/theme.inc' --- includes/theme.inc 2008-02-06 19:38:26 +0000 +++ includes/theme.inc 2008-02-15 03:29:26 +0000 @@ -628,7 +628,7 @@ function theme() { // call_user_func_array. $args = array(&$variables, $hook); foreach ($info['preprocess functions'] as $preprocess_function) { - if (function_exists($preprocess_function)) { + if (registry_check_function($preprocess_function)) { call_user_func_array($preprocess_function, $args); } } === modified file 'includes/xmlrpcs.inc' --- includes/xmlrpcs.inc 2007-12-31 08:54:36 +0000 +++ includes/xmlrpcs.inc 2008-02-14 00:43:32 +0000 @@ -202,7 +202,7 @@ function xmlrpc_server_call($xmlrpc_serv } } - if (!function_exists($method)) { + if (!registry_check_function($method)) { return xmlrpc_error(-32601, t('Server error. Requested function %method does not exist.', array("%method" => $method))); } // Call the mapped function === modified file 'modules/system/system.install' --- modules/system/system.install 2008-01-30 20:30:35 +0000 +++ modules/system/system.install 2008-02-15 03:43:32 +0000 @@ -551,6 +551,8 @@ function system_schema() { $schema['cache_page']['description'] = t('Cache table used to store compressed pages for anonymous users, if page caching is enabled.'); $schema['cache_menu'] = $schema['cache']; $schema['cache_menu']['description'] = t('Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.'); + $schema['cache_registry'] = $schema['cache']; + $schema['cache_registry']['description'] = t('Cache table for the code registry system to remember what code files need to be loaded on any given page.'); $schema['files'] = array( 'description' => t('Stores information for uploaded files.'), @@ -773,10 +775,6 @@ function system_schema() { 'type' => 'int', 'not null' => TRUE, 'default' => 0), - 'file' => array( - 'description' => t('The file to include for this element, usually the page callback function lives in this file.'), - 'type' => 'text', - 'size' => 'medium') ), 'indexes' => array( 'fit' => array('fit'), @@ -945,6 +943,48 @@ function system_schema() { ), 'primary key' => array('mlid'), ); + $schema['registry'] = array( + 'description' => t("Each record is a function / class / interface name and the file it is in"), + 'fields' => array( + 'name' => array( + 'description' => t('The name of the function / class / interface.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'description' => t('Either function or class or interface'), + 'type' => 'varchar', + 'length' => 9, + 'not null' => TRUE, + 'default' => '', + ), + 'module' => array( + 'description' => t('Name of the module this file belongs to.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'hook' => array( + 'description' => t('Name of the function minus the name of the module'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'file' => array( + 'description' => t('Path to the file'), + 'type' => 'text', + 'size' => 'medium', + ), + ), + 'primary key' => array('name', 'type'), + 'indexes' => array( + 'module_implements' => array('type', 'hook'), + ), + ); $schema['sessions'] = array( 'description' => t("Drupal's session handlers read and write into the sessions table. Each record represents a user session, either anonymous or authenticated."), @@ -2467,6 +2507,11 @@ function system_update_6047() { return $ret; } +function system_update_7000() { + $ret = array(); + db_drop_field($ret, 'menu_router', 'file'); +} + /** * @} End of "defgroup updates-5.x-to-6.x" * The next series of updates should start at 7000. === modified file 'modules/system/system.module' --- modules/system/system.module 2008-02-12 14:06:58 +0000 +++ modules/system/system.module 2008-02-15 02:20:06 +0000 @@ -1285,6 +1285,13 @@ function system_action_info() { } /** + * Implementation of hook_hooks(). + */ +function system_hooks() { + return array('__form_alter'); +} + +/** * Menu callback. Display an overview of available and configured actions. */ function system_actions_manage() { === modified file 'update.php' --- update.php 2008-02-03 18:41:16 +0000 +++ update.php 2008-02-15 03:35:22 +0000 @@ -422,6 +422,41 @@ function update_create_batch_table() { } /** + * This is part of the Drupal 6.x to 7.x migration. + */ +function update_create_registry_tables() { + $schema['registry'] = array( + 'fields' => array( + 'name' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'type' => array('type' => 'varchar', 'length' => 9, 'not null' => TRUE, 'default' => ''), + 'module' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'hook' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'file' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), + ), + 'primary key' => array('name', 'type'), + 'indexes' => array( + 'module_implements' => array('type', 'hook'), + ), + ); + $schema['cache_registry'] = array( + 'fields' => array( + 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'), + 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'headers' => array('type' => 'text', 'not null' => FALSE), + 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0) + ), + 'indexes' => array('expire' => array('expire')), + 'primary key' => array('cid'), + ); + $ret = array(); + db_create_table($ret, 'cache_registry', $schema['cache_registry']); + db_create_table($ret, 'registry', $schema['registry']); + drupal_rebuild_code_registry(); +} + +/** * Disable anything in the {system} table that is not compatible with the * current version of Drupal core. */