summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/bootstrap.inc182
-rw-r--r--includes/common.inc13
-rw-r--r--includes/form.inc13
-rw-r--r--includes/install.inc5
-rw-r--r--includes/mail.inc2
-rw-r--r--includes/menu.inc38
-rw-r--r--includes/module.inc55
-rw-r--r--includes/theme.inc6
-rw-r--r--includes/xmlrpcs.inc2
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