diff options
-rw-r--r-- | includes/module.inc | 40 | ||||
-rw-r--r-- | modules/simpletest/tests/module.test | 20 | ||||
-rw-r--r-- | modules/simpletest/tests/module_test.file.inc | 2 | ||||
-rw-r--r-- | modules/simpletest/tests/module_test.module | 39 |
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) { |