summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/module.inc40
-rw-r--r--modules/simpletest/tests/module.test20
-rw-r--r--modules/simpletest/tests/module_test.file.inc2
-rw-r--r--modules/simpletest/tests/module_test.module39
4 files changed, 93 insertions, 8 deletions
diff --git a/includes/module.inc b/includes/module.inc
index 6cbed1fe0..aa061e195 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -592,7 +592,20 @@ function module_disable($module_list, $disable_dependents = TRUE) {
* implemented in that module.
*/
function module_hook($module, $hook) {
- return function_exists($module . '_' . $hook);
+ $function = $module . '_' . $hook;
+ if (function_exists($function)) {
+ return TRUE;
+ }
+ // If the hook implementation does not exist, check whether it may live in an
+ // optional include file registered via hook_hook_info().
+ $hook_info = module_hook_info();
+ if (isset($hook_info[$hook]['group'])) {
+ module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
+ if (function_exists($function)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
}
/**
@@ -660,7 +673,9 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
$list = module_list(FALSE, FALSE, $sort);
foreach ($list as $module) {
$include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
- if (module_hook($module, $hook)) {
+ // Since module_hook() may needlessly try to load the include file again,
+ // function_exists() is used directly here.
+ if (function_exists($module . '_' . $hook)) {
$implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
}
}
@@ -678,9 +693,11 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
module_load_include('inc', $module, "$module.$group");
}
// It is possible that a module removed a hook implementation without the
- // implementations cache being rebuilt yet, so we check module_hook() on
- // each request to avoid undefined function errors.
- if (!module_hook($module, $hook)) {
+ // implementations cache being rebuilt yet, so we check whether the
+ // function exists on each request to avoid undefined function errors.
+ // Since module_hook() may needlessly try to load the include file again,
+ // function_exists() is used directly here.
+ if (!function_exists($module . '_' . $hook)) {
// Clear out the stale implementation from the cache and force a cache
// refresh to forget about no longer existing hook implementations.
unset($implementations[$hook][$module]);
@@ -696,9 +713,17 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
* Retrieve a list of what hooks are explicitly declared.
*/
function module_hook_info() {
- $hook_info = &drupal_static(__FUNCTION__, array());
+ // This function is indirectly invoked from bootstrap_invoke_all(), in which
+ // case common.inc, subsystems, and modules are not loaded yet, so it does not
+ // make sense to support hook groups resp. lazy-loaded include files prior to
+ // full bootstrap.
+ if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) {
+ return array();
+ }
+ $hook_info = &drupal_static(__FUNCTION__);
- if (empty($hook_info)) {
+ if (!isset($hook_info)) {
+ $hook_info = array();
$cache = cache_get('hook_info', 'cache_bootstrap');
if ($cache === FALSE) {
// Rebuild the cache and save it.
@@ -768,6 +793,7 @@ function module_invoke() {
return call_user_func_array($module . '_' . $hook, $args);
}
}
+
/**
* Invoke a hook in all enabled modules that implement it.
*
diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test
index d3e3dbf1b..711a739ab 100644
--- a/modules/simpletest/tests/module.test
+++ b/modules/simpletest/tests/module.test
@@ -112,6 +112,26 @@ class ModuleUnitTest extends DrupalWebTestCase {
}
/**
+ * Test that module_invoke() can load a hook defined in hook_hook_info().
+ */
+ function testModuleInvoke() {
+ module_enable(array('module_test'), FALSE);
+ $this->resetAll();
+ $this->drupalGet('module-test/hook-dynamic-loading-invoke');
+ $this->assertText('success!', t('module_invoke() dynamically loads a hook defined in hook_hook_info().'));
+ }
+
+ /**
+ * Test that module_invoke_all() can load a hook defined in hook_hook_info().
+ */
+ function testModuleInvokeAll() {
+ module_enable(array('module_test'), FALSE);
+ $this->resetAll();
+ $this->drupalGet('module-test/hook-dynamic-loading-invoke-all');
+ $this->assertText('success!', t('module_invoke_all() dynamically loads a hook defined in hook_hook_info().'));
+ }
+
+ /**
* Test dependency resolution.
*/
function testDependencyResolution() {
diff --git a/modules/simpletest/tests/module_test.file.inc b/modules/simpletest/tests/module_test.file.inc
index f3c354f35..72210d768 100644
--- a/modules/simpletest/tests/module_test.file.inc
+++ b/modules/simpletest/tests/module_test.file.inc
@@ -10,5 +10,5 @@
* Implements hook_test_hook().
*/
function module_test_test_hook() {
-
+ return array('module_test' => 'success!');
}
diff --git a/modules/simpletest/tests/module_test.module b/modules/simpletest/tests/module_test.module
index b1f7361a0..d3f46e2cb 100644
--- a/modules/simpletest/tests/module_test.module
+++ b/modules/simpletest/tests/module_test.module
@@ -52,6 +52,45 @@ function module_test_hook_info() {
}
/**
+ * Implements hook_menu().
+ */
+function module_test_menu() {
+ $items['module-test/hook-dynamic-loading-invoke'] = array(
+ 'title' => 'Test hook dynamic loading (invoke)',
+ 'page callback' => 'module_test_hook_dynamic_loading_invoke',
+ 'access arguments' => array('access content'),
+ );
+ $items['module-test/hook-dynamic-loading-invoke-all'] = array(
+ 'title' => 'Test hook dynamic loading (invoke_all)',
+ 'page callback' => 'module_test_hook_dynamic_loading_invoke_all',
+ 'access arguments' => array('access content'),
+ );
+ return $items;
+}
+
+/**
+ * Page callback for 'hook dynamic loading' test.
+ *
+ * If the hook is dynamically loaded correctly, the menu callback should
+ * return 'success!'.
+ */
+function module_test_hook_dynamic_loading_invoke() {
+ $result = module_invoke('module_test', 'test_hook');
+ return $result['module_test'];
+}
+
+/**
+ * Page callback for 'hook dynamic loading' test.
+ *
+ * If the hook is dynamically loaded correctly, the menu callback should
+ * return 'success!'.
+ */
+function module_test_hook_dynamic_loading_invoke_all() {
+ $result = module_invoke_all('test_hook');
+ return $result['module_test'];
+}
+
+/**
* Implements hook_modules_enabled().
*/
function module_test_modules_enabled($modules) {