diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/bootstrap.inc | 182 | ||||
-rw-r--r-- | includes/common.inc | 13 | ||||
-rw-r--r-- | includes/form.inc | 13 | ||||
-rw-r--r-- | includes/install.inc | 5 | ||||
-rw-r--r-- | includes/mail.inc | 2 | ||||
-rw-r--r-- | includes/menu.inc | 38 | ||||
-rw-r--r-- | includes/module.inc | 55 | ||||
-rw-r--r-- | includes/theme.inc | 6 | ||||
-rw-r--r-- | includes/xmlrpcs.inc | 2 |
9 files changed, 258 insertions, 58 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 49659f8ca..c5a8ba404 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -949,6 +949,9 @@ function _drupal_bootstrap($phase) { // Initialize the default database. require_once './includes/database.inc'; db_set_active(); + // Register autoload functions so that we can access classes and interfaces. + spl_autoload_register('drupal_autoload_class'); + spl_autoload_register('drupal_autoload_interface'); break; case DRUPAL_BOOTSTRAP_ACCESS: @@ -1134,3 +1137,182 @@ function ip_address() { return $ip_address; } + +/** + * @ingroup registry + * @{ + */ + +/** + * Confirm that a function is available. + * + * If the function is already available, this function does nothing. + * If the function is not available, it tries to load the file where the + * function lives. If the file is not available, it returns false, so that it + * can be used as a drop-in replacement for function_exists(). + * + * @param $function + * The name of the function to check or load. + * @return + * TRUE if the function is now available, FALSE otherwise. + */ +function drupal_function_exists($function) { + static $checked = array(); + + if (defined('MAINTENANCE_MODE')) { + return function_exists($function); + } + + if (isset($checked[$function])) { + return $checked[$function]; + } + $checked[$function] = FALSE; + + if (function_exists($function)) { + registry_mark_code('function', $function); + $checked[$function] = TRUE; + return TRUE; + } + + $file = db_result(db_query("SELECT filename FROM {registry} WHERE name = '%s' AND type = '%s'", $function, 'function')); + if ($file) { + require_once($file); + $checked[$function] = function_exists($function); + if ($checked[$function]) { + registry_mark_code('function', $function); + } + } + + return $checked[$function]; +} + +/** + * Confirm that an interface is available. + * + * This function parallels drupal_function_exists(), but is rarely + * called directly. Instead, it is registered as an spl_autoload() + * handler, and PHP calls it for us when necessary. + * + * @param $interface + * The name of the interface to check or load. + * @return + * TRUE if the interface is currently available, FALSE otherwise. + */ +function drupal_autoload_interface($interface) { + return _registry_check_code('interface', $interface); +} + +/** + * Confirm that a class is available. + * + * This function parallels drupal_function_exists(), but is rarely + * called directly. Instead, it is registered as an spl_autoload() + * handler, and PHP calls it for us when necessary. + * + * @param $class + * The name of the class to check or load. + * @return + * TRUE if the class is currently available, FALSE otherwise. + */ +function drupal_autoload_class($class) { + return _registry_check_code('class', $class); +} + +/** + * Helper for registry_check_{interface, class}. + */ +function _registry_check_code($type, $name) { + $file = db_result(db_query("SELECT filename FROM {registry} WHERE name = '%s' AND type = '%s'", $name, $type)); + if ($file) { + require_once($file); + registry_mark_code($type, $name); + return TRUE; + } +} + +/** + * Collect the resources used for this request. + * + * @param $type + * The type of resource. + * @param $name + * The name of the resource. + * @param $return + * Boolean flag to indicate whether to return the resources. + */ +function registry_mark_code($type, $name, $return = FALSE) { + static $resources = array(); + + if ($type && $name) { + if (!isset($resources[$type])) { + $resources[$type] = array(); + } + if (!in_array($name, $resources[$type])) { + $resources[$type][] = $name; + } + } + + if ($return) { + return $resources; + } +} + +/** + * Rescan all enabled modules and rebuild the registry. + * + * Rescans all code in modules or includes directory, storing a mapping of + * each function, file, and hook implementation in the database. + */ +function drupal_rebuild_code_registry() { + require_once './includes/registry.inc'; + _drupal_rebuild_code_registry(); +} + +/** + * Save hook implementations cache. + * + * @param $hook + * Array with the hook name and list of modules that implement it. + * @param $write_to_persistent_cache + * Whether to write to the persistent cache. + */ +function registry_cache_hook_implementations($hook, $write_to_persistent_cache = FALSE) { + static $implementations; + + if ($hook) { + // Newer is always better, so overwrite anything that's come before. + $implementations[$hook['hook']] = $hook['modules']; + } + + if ($write_to_persistent_cache === TRUE) { + cache_set('hooks', $implementations, 'cache_registry'); + } +} + +/** + * Save the files required by the registry for this path. + */ +function registry_cache_path_files() { + if ($used_code = registry_mark_code(NULL, NULL, TRUE)) { + $files = array(); + $type_sql = array(); + $params = array(); + foreach ($used_code as $type => $names) { + $type_sql[] = "(name IN (" . db_placeholders($names, 'varchar') . ") AND type = '%s')"; + $params = array_merge($params, $names); + $params[] = $type; + } + $res = db_query("SELECT DISTINCT filename FROM {registry} WHERE " . implode(' OR ', $type_sql), $params); + while ($row = db_fetch_object($res)) { + $files[] = $row->filename; + } + if ($files) { + $menu = menu_get_item(); + cache_set('registry:' . $menu['path'], implode(';', $files), 'cache_registry'); + } + } +} + +/** + * @} End of "ingroup registry". + */ diff --git a/includes/common.inc b/includes/common.inc index 371f3443f..710d2e3c2 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1460,11 +1460,15 @@ function l($text, $path, $options = array()) { * react to the closing of the page by calling hook_exit(). */ function drupal_page_footer() { + if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) { page_set_cache(); } module_invoke_all('exit'); + + registry_cache_hook_implementations(FALSE, TRUE); + registry_cache_path_files(); } /** @@ -2700,7 +2704,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 (drupal_function_exists($function)) { $elements = $function($elements); } } @@ -2762,7 +2766,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 (drupal_function_exists($function)) { $content = $function($content, $elements); } } @@ -3142,7 +3146,7 @@ function drupal_uninstall_schema($module) { */ function drupal_get_schema_unprocessed($module, $table = NULL) { // Load the .install file to get hook_schema. - module_load_include('install', $module); + module_load_install($module); $schema = module_invoke($module, 'schema'); if (!is_null($table) && isset($schema[$table])) { @@ -3528,6 +3532,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(); system_theme_data(); @@ -3536,7 +3541,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); diff --git a/includes/form.inc b/includes/form.inc index bb02accab..94aa380b1 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -327,7 +327,7 @@ function drupal_retrieve_form($form_id, &$form_state) { // 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 (!drupal_function_exists($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. @@ -348,6 +348,7 @@ function drupal_retrieve_form($form_id, &$form_state) { } if (isset($form_definition['callback'])) { $callback = $form_definition['callback']; + drupal_function_exists($callback); } } @@ -504,13 +505,13 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { $form += _element_info('form'); if (!isset($form['#validate'])) { - if (function_exists($form_id . '_validate')) { + if (drupal_function_exists($form_id . '_validate')) { $form['#validate'] = array($form_id . '_validate'); } } if (!isset($form['#submit'])) { - if (function_exists($form_id . '_submit')) { + if (drupal_function_exists($form_id . '_submit')) { // We set submit here so that it can be altered. $form['#submit'] = array($form_id . '_submit'); } @@ -712,7 +713,7 @@ function _form_validate($elements, &$form_state, $form_id = NULL) { // #value data. elseif (isset($elements['#element_validate'])) { foreach ($elements['#element_validate'] as $function) { - if (function_exists($function)) { + if (drupal_function_exists($function)) { $function($elements, $form_state, $complete_form); } } @@ -749,7 +750,7 @@ function form_execute_handlers($type, &$form, &$form_state) { } foreach ($handlers as $function) { - if (function_exists($function)) { + if (drupal_function_exists($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 @@ -1032,7 +1033,7 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state, $com // checkboxes and files. if (isset($form['#process']) && !$form['#processed']) { foreach ($form['#process'] as $process) { - if (function_exists($process)) { + if (drupal_function_exists($process)) { $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form); } } diff --git a/includes/install.inc b/includes/install.inc index 9398077a1..54bfd4bc4 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -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; diff --git a/includes/mail.inc b/includes/mail.inc index 56c765e7c..c2394a414 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -115,7 +115,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N // 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 (drupal_function_exists($function = $module . '_mail')) { $function($key, $message, $params); } diff --git a/includes/menu.inc b/includes/menu.inc index 2e57234dd..1a52d3da4 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -339,11 +339,16 @@ function menu_execute_active_handler($path = NULL) { menu_rebuild(); } if ($router_item = menu_get_item($path)) { + $cache = cache_get('registry:' . $router_item['path'], 'cache_registry'); + if (!empty($cache->data)) { + foreach(explode(';', $cache->data) as $file) { + require_once($file); + } + } if ($router_item['access']) { - if ($router_item['file']) { - require_once($router_item['file']); + if (drupal_function_exists($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; @@ -1665,7 +1670,7 @@ function menu_router_build($reset = FALSE) { // We need to manually call each module so that we can know which module // a given item came from. $callbacks = array(); - foreach (module_implements('menu') as $module) { + foreach (module_implements('menu', NULL, TRUE) as $module) { $router_items = call_user_func($module . '_menu'); if (isset($router_items) && is_array($router_items)) { foreach (array_keys($router_items) as $path) { @@ -2201,12 +2206,12 @@ function _menu_router_build($callbacks) { $load_functions[$k] = NULL; } else { - if (function_exists($matches[1] . '_to_arg')) { + if (drupal_function_exists($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 (drupal_function_exists($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' @@ -2293,12 +2298,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']; - } } } } @@ -2326,34 +2325,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); diff --git a/includes/module.inc b/includes/module.inc index d0f100298..e62558af4 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -221,7 +221,7 @@ function _module_build_dependencies($files) { */ function module_exists($module) { $list = module_list(); - return array_key_exists($module, $list); + return isset($list[$module]); } /** @@ -231,7 +231,14 @@ function module_load_install($module) { // Make sure the installation API is available include_once './includes/install.inc'; - module_load_include('install', $module); + $file = module_load_include('install', $module); + // Ensure that you can module_invoke something inside the newly-loaded + // file during install. + $module_list = module_list(); + if (!isset($module_list[$module])) { + $module_list[$module]['filename'] = $file; + module_list(TRUE, FALSE, FALSE, $module_list); + } } /** @@ -253,6 +260,7 @@ function module_load_include($type, $module, $name = NULL) { if (is_file($file)) { require_once $file; + return $file; } else { return FALSE; @@ -292,7 +300,7 @@ function module_enable($module_list) { // Refresh the module list to include the new enabled module. module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. - module_implements('', FALSE, TRUE); + drupal_rebuild_code_registry(); } foreach ($invoke_modules as $module) { @@ -301,7 +309,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 (drupal_function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { node_access_needs_rebuild(TRUE); } } @@ -333,7 +341,7 @@ function module_disable($module_list) { // Refresh the module list to exclude the disabled modules. module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. - module_implements('', FALSE, TRUE); + drupal_rebuild_code_registry(); } // If there remains no more node_access module, rebuilding will be @@ -376,7 +384,13 @@ function module_disable($module_list) { * implemented in that module. */ function module_hook($module, $hook) { - return function_exists($module . '_' . $hook); + $function = $module . '_' . $hook; + if (defined('MAINTENANCE_MODE')) { + return function_exists($function); + } + else { + return drupal_function_exists($function); + } } /** @@ -395,22 +409,26 @@ 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 = array(); if ($refresh) { $implementations = array(); - return; + } + else if (!defined('MAINTENANCE_MODE') && empty($implementations)) { + if ($cache = cache_get('hooks', 'cache_registry')) { + $implementations = $cache->data; + } } if (!isset($implementations[$hook])) { $implementations[$hook] = array(); - $list = module_list(FALSE, TRUE, $sort); - foreach ($list as $module) { + foreach (module_list() as $module) { if (module_hook($module, $hook)) { $implementations[$hook][] = $module; } } } + registry_cache_hook_implementations(array('hook' => $hook, 'modules' => $implementations[$hook])); // The explicit cast forces a copy to be made. This is needed because // $implementations[$hook] is only a reference to an element of @@ -438,9 +456,8 @@ function module_invoke() { $module = $args[0]; $hook = $args[1]; unset($args[0], $args[1]); - $function = $module . '_' . $hook; if (module_hook($module, $hook)) { - return call_user_func_array($function, $args); + return call_user_func_array($module . '_' . $hook, $args); } } /** @@ -461,12 +478,14 @@ function module_invoke_all() { $return = array(); foreach (module_implements($hook) as $module) { $function = $module . '_' . $hook; - $result = call_user_func_array($function, $args); - if (isset($result) && is_array($result)) { - $return = array_merge_recursive($return, $result); - } - else if (isset($result)) { - $return[] = $result; + if (drupal_function_exists($function)) { + $result = call_user_func_array($function, $args); + if (isset($result) && is_array($result)) { + $return = array_merge_recursive($return, $result); + } + else if (isset($result)) { + $return[] = $result; + } } } diff --git a/includes/theme.inc b/includes/theme.inc index d675e1564..3b4d1dc1c 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -177,7 +177,9 @@ function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme } } - $registry_callback($theme, $base_theme, $theme_engine); + if (drupal_function_exists($registry_callback)) { + $registry_callback($theme, $base_theme, $theme_engine); + } } /** @@ -628,7 +630,7 @@ function theme() { // call_user_func_array. $args = array(&$variables, $hook); foreach ($info['preprocess functions'] as $preprocess_function) { - if (function_exists($preprocess_function)) { + if (drupal_function_exists($preprocess_function)) { call_user_func_array($preprocess_function, $args); } } diff --git a/includes/xmlrpcs.inc b/includes/xmlrpcs.inc index 21509d494..ffa5fd8c3 100644 --- a/includes/xmlrpcs.inc +++ b/includes/xmlrpcs.inc @@ -202,7 +202,7 @@ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { } } - if (!function_exists($method)) { + if (!drupal_function_exists($method)) { return xmlrpc_error(-32601, t('Server error. Requested function @method does not exist.', array("@method" => $method))); } // Call the mapped function |