Sometimes when I try to apply a hook, just to realise it's supposed to be in a module file.
Is there a way of knowing what hooks can be implemented in a theme's template.php file, or one in a module?
Sometimes when I try to apply a hook, just to realise it's supposed to be in a module file.
Is there a way of knowing what hooks can be implemented in a theme's template.php file, or one in a module?
Generally speaking, only alter hooks can be implemented by themes, which means hooks like hook_form_alter()
and hook_menu_alter()
, or in short all those hooks that are invoked through drupal_alter()
in Drupal 7 and lower (ModuleHandler()::alter()
or ThemeManager::alter()
in Drupal 8).
Other hooks, which are invoked by module_invoke_all()
(ModuleHandler::invokeAll()
in Drupal 8), don't get invoked for themes simply for fact the code doesn't check if the currently enabled theme defines any hook.
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$result = call_user_func_array($function, $args);
if (isset($result) && is_array($result)) {
$return = array_merge_recursive($return, $result);
}
elseif (isset($result)) {
$return[] = $result;
}
}
}
In Drupal 8, where the ModuleHandler
class invokes hooks implemented from modules and the ThemeManager
class invokes hooks implemented by themes, only the first class implements invoke()
and invokeAll()
. This means that in Drupal 8 theme hooks aren't invoked, by Drupal core.
This is valid for Drupal core hooks, and mostly all the hooks used by contributed modules. It is then up to a module to verify a hook is implemented by a theme too, and invoke it. This is what the Views module does.
// Let modules modify the view just prior to rendering it.
foreach (module_implements('views_pre_render') as $module) {
$function = $module . '_views_pre_render';
$function($this);
}
// Let the themes play too, because pre render is a very themey thing.
foreach ($GLOBALS['base_theme_info'] as $base) {
$function = $base->name . '_views_pre_render';
if (function_exists($function)) {
$function($this);
}
}
$function = $GLOBALS['theme'] . '_views_pre_render';
if (function_exists($function)) {
$function($this);
}
For hooks used by contributed modules, you need to check the code used to invoke hooks. Chances are that, for themes, only the alter hooks are invoked; in some cases, other hooks could be implemented by themes.
Keep in mind that in the case of themes, not all the enabled themes are checked for hook implementations, contrary to what happens with modules. Only the currently used theme and the base themes are checked, as done from the Views module.
theme()
). If it hasn’t been initialized no alter hooks in any theme will be executed.
Commented
Apr 8, 2020 at 9:24
theme()
doesn't change the theme used for the page, but it invokes a function to render data. It doesn't change the theme, for example, from Garland to Minelli.
drupal_alter()
you see that it only invokes alter-hooks in the theme if drupal_theme_initialize()
was called before. If it wasn’t then there is no active theme (yet) and thus no hooks called. At least in D7 there is no guarantee when drupal_theme_initialize()
is called the first time in a request.
Commented
Apr 8, 2020 at 12:45