hook_module_implements_alter()
is invoked only for those hooks that are invoked via ModuleHandler::alter()
, ModuleHandler::invokeAll()
, and ModuleHandler::invokeAllWith()
. All those methods call ModuleHandler::getImplementationInfo()
, which calls ModuleHandler::buildImplementationInfo()
, which contains the following code. (See the line after the Verify implementations that were added or modified. comment.)
// Allow modules to change the weight of specific implementations, but avoid
// an infinite loop.
if ($hook != 'module_implements_alter') {
// Remember the original implementations, before they are modified with
// hook_module_implements_alter().
$implementations_before = $implementations;
// Verify implementations that were added or modified.
$this->alter('module_implements', $implementations, $hook);
// Verify new or modified implementations.
foreach (array_diff_assoc($implementations, $implementations_before) as $module => $group) {
// If an implementation of hook_module_implements_alter() changed or
// added a group, the respective file needs to be included.
if ($group) {
$this->loadInclude($module, 'inc', "{$module}.{$group}");
}
// If a new implementation was added, verify that the function exists.
if (!function_exists($module . '_' . $hook)) {
throw new \RuntimeException("An invalid implementation {$module}_{$hook} was added by hook_module_implements_alter()");
}
}
}
Registry::processExtension()
creates the list of preprocess hooks implemented for a single theme hook with the following code.
foreach ($prefixes as $prefix) {
if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
$info['preprocess functions'][] = $prefix . '_preprocess';
}
if (function_exists($prefix . '_preprocess_' . $hook)) {
$info['preprocess functions'][] = $prefix . '_preprocess_' . $hook;
}
}
$prefixes
is initialized from the following code. In particular, see the if ($type == 'module') { /* … */ }
part, which is the one that allows to modules to add their own variable preprocessors (a.k.a. preprocess hooks).
(I left only the relevant comment to make the code shorter. All the comments can be read in the linked documentation page, which shows also the method code.)
if ($type == 'module') {
$prefixes[] = 'template';
// Add all modules so they can intervene with their own variable
// preprocessors. This allows them to provide variable preprocessors
// even if they are not the owner of the current hook.
$prefixes = array_merge($prefixes, $module_list);
}
elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
$prefixes[] = $name . '_engine';
$prefixes[] = $theme;
}
else {
$prefixes[] = $name;
}
$module_list is initialized with $module_list = array_keys($this->moduleHandler->getModuleList());
The code that invokes the preprocess hooks does not use the methods from the ModuleHandler
class I listed earlier, since it builds the list of preprocess hooks to invoke using Registry::processExtension()
, which means hook_module_implements_alter()
is not invoked for preprocess hooks.
The list of modules returned by ModuleHandler::getModuleList()
is ordered by the modules weight, though. This means that changing a module weight will change the order preprocess hooks are invoked.