Score:0

Changing order of execution of hook_preprocess_html

ru flag

I have 2 custom modules in Drupal 9, let's called them module A and module B.

Each module has a fully working hook_preprocess_html function defined in the .module file. The hook_preprocess_html function in module A runs first, followed by the function in module B.

How can I update Module A so the hook_preprocess_html function in there runs after the one in module B?

I have tried to implement hook_module_implements_alter(&$implementations, $hook) but it is not even showing the hook_preprocess_html hook. This preprocess_html seems not to apply to the module_implements_alter hook.

Many thanks for you help!

4uk4 avatar
cn flag
If this hook doesn't help I think there is still the module weight. See https://drupal.stackexchange.com/questions/186506/how-to-reduce-increase-modules-weight-on-the-module-install-process
Score:0
aq flag

Is there a hook_preprocess_hook in the list? As the doc says hook_module_implements_alter will only show the base hooks and hook_preprocess_html is a variant of hook_preprocess_hook

So you would need to change the weight for hook_preprocess_hook

AlastairHoward avatar
ru flag
Thanks for your reply. No, hook_preprocess_hook is not in the list, which I guess is explained by apaderno.
Score:0
us flag

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.

AlastairHoward avatar
ru flag
Thanks, that makes sense. Does this mead that they only way to affect the order of preprocessors is to set a module weight at install?
apaderno avatar
us flag
If the list of modules is taken from *container.modules*, or from a function/method that returns the value of that container parameter, yes. That includes the code that calls `ModuleHandler::getModuleList()`, which is called from the method that uses the last code lines I show in the answer.
apaderno avatar
us flag
I am going to expand the answer to show that the method that creates the list of preprocess hooks uses `ModuleHandler::getModuleList()`, which would make more evident the code is not using `ModuleHandler::invokeAll()`, for example.
apaderno avatar
us flag
Done: Now the answer should be clearer about changing a module weight to change the order the preprocess hooks are invoked.
AlastairHoward avatar
ru flag
Excellent, thank you. I changed the module weight and now I'm getting things in teh right order for me. I am surprised there's no other way, as it's not always possible to change a module weight by uninstalling/ re)installing once live.
apaderno avatar
us flag
@AlastairHoward You should not need to uninstall/reinstall a module to change its weight. It is enough you call [`module_set_weight()`](https://api.drupal.org/api/drupal/core%21includes%21module.inc/function/module_set_weight/9) inside a new `hook_update_N()` implementation.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.