Drupal 7 brings some important API changes and enhancements to the table, one of them being the ability to control precisely the order of execution of a particular hook between modules.
I was asked by a client last week if it is possible to control execution of specific hooks between modules. Here is the (very precisely) framed question:
Can I ask if it is possible in Drupal to affect the order in which hooks are fired beyond the module weight system? What if I have hook x and hook y and module 1 and 2. I want hook x to run in module 1 and then hook x to run in module 2 BUT i want hook y to run in module 2, then hook y in module 1. I only seem to be able to influence hook order at module level so that hook x and hook y both run in module 1 before hook x and hook y in module 2!
And my answer was yupp, its possible, because fortunately we chose Drupal 7 for our implementation (we started on this site around mid January this year, and D7 was just launched. To us, it was a bold decision to chose D7 at that time with very limited contributed modules having a D7 version, but the choice has paid off really well with all the new abilities that D7 brings to the table).
As a Drupal developer, you would already be aware that Drupal assigns a weight to each module (that gets stored in system table). All hooks on the modules are by default invoked in order of module weights and in fact, this is the only way to control “hooking” order for modules in D6. Which means that for a module with lower weight, all hooks would be invoked before than the corresponding hooks on a module that has a higher weight. You cannot control the order of “hooking” on a per-hook basis in D6.
But D7 brings with itself this wonderful new capability of controlling “hooking” order on a per-hook per-module basis with the new hook called: hook_module_implements_alter.
Basically with this new hook, you can re-order the module hooking order for a specific hook. Here’s an example of implementing this hook:
{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function mymodule_module_implements_alter(&$implementations, $hook) {
//Any change here requires Caches to be cleared.
switch ($hook) {
case ‘form_alter’:
$m2 = $implementations[‘m2’];
unset($implementations[‘m2’]);
$m3 = $implementations[‘m3’];
unset($implementations[‘m3’]);
//hook_form_alter would be called on module m3 before module m2.
$implementations[‘m3’] = $m3;
$implementations[‘m2’] = $m2;
break;
case ‘menu’:
$m2 = $implementations[‘m2’];
unset($implementations[‘m2’]);
$m3 = $implementations[‘m3’];
unset($implementations[‘m3’]);
//hook_menu would be called on module m2 before module m3.
$implementations[‘m2’] = $m2;
$implementations[‘m3’] = $m3;
break;
}
}
{/syntaxhighlighter}
The code is pretty self-explanatory I believe. The fact that Drupal iterates through $implementations with a foreach loop which PHP iterates in the order that the items were added ensures that hooks added later are invoked later. So, controlling the order of hooks just requires us to re-order the $implementations array in the desired sequence.
I would like to insist that this ability is available in only Drupal 7 and later versions. In Drupal 6, module weights control the order of hook execution which means you cannot control it on a per-hook basis between various modules.
The attached code below demonstrates this new ability in Drupal 7. It contains 3 modules. Module 1 dictates the order in which hook_form_alter is invoked on Module 2 and Module 3 by implementing hook_module_implements_alter.
While creating this test code for the purpose of this blog entry, I found a bug in Drupal’s core, that I have reported here. At the time I wrote this blog entry, Drupal does not honor the changes made by hook_module_implements_alter for hook_form_form_id_alter. This is a major bug in my opinion because there would be instances where you want hook_form_form_id_alter to be invoked in different order on different forms.
A solution is to implement hook_form_alter instead whose order changes with hook_module_implements_alter are honored by Drupal. But this would mean that you would not be able to control the order of altering forms on a per form basis. I hope the Drupal team fixes this issue soon. Currently, if you want to do so, your best bet would be not to implement hook_form_form_id_alter in modules where you want to change the order. But implement hook_form_alter in another module which checks $form_id and manually invokes hook_form_form_id_alter on your modules where you want to control the order of altering forms.
Thanks for this, you save my life! 🙂
Hello, Rahul. Thank you for posting this article. I’m trying to use these ideas to cause Drupal to call a hook function for custom module, instead of the same hook function for a contrib module. To get started, I tried:
function mymodule_module_implements_alter( &$implementations, $hook ) {
drupal_set_message( '$hook = ' . $hook . ', $implementations = ' . print_r( $implementations, true ) );
}
I expected to get an array listing all of the module names, but instead the only output is:
$hook = field_display_alter, $implementations = Array ( [node] => [token] => )
$hook = query_alter, $implementations = Array ( [node] => )
Any ideas what might be going wrong? Thank you!
Hello !
In the same logic, is it possible to know / affect order of modules installation ?? example :
I have two custom “content-type” modules (customA and customB, without dependencies) if user try to enable twice modules at same time, I want the customA must be installed before the customB. But I don’t want to use dependencies to do it, because the two modules can work alone. But if customA is installed, customB install some additional fields linked to customA installed fields.
Thanks for any suggestion
Thanks for your answer, it’s really interesting to see possibilities drupal7 offer 🙂 I’ll try it as soon as possible.