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.