Hook Ordering
When to Use
Your implementation must run before or after another module's, or you must stop another module's implementation from running. Since 11.2.0 you declare this in attributes;
hook_module_implements_alter()is on its way out.
Decision
Default order, from core/core.api.php (@defgroup hooks): module weight, then alphabetical by module name. Within one module, procedural implementations run before OOP implementations of the same hook (11.2.0, change record 3515207).
| If you need... | Use... | Why |
|---|---|---|
| Your implementation first or last among all modules | order: Order::First or order: Order::Last on your #[Hook] |
FirstOrLast operation moves your identifier to the array start or end |
| Yours before or after named modules | order: new OrderBefore(modules: ['a', 'b']) / new OrderAfter(modules: [...]) |
BeforeOrAfter splices yours immediately before the earliest, or after the latest, of the named modules |
| Yours before or after one specific class method | new OrderAfter(classesAndMethods: [[Foo::class, 'method']]) |
Same operation, finer target. Use [ProceduralCall::class, 'function_name'] to target a procedural implementation |
| Another module's implementation moved, without touching that module | #[ReorderHook(hook, class, method, order)] on any class or method in your src/Hook/ |
Runs after every #[Hook(order:)] is applied (core.api.php, "ordering_other_module_hooks"). 11.2.0, change record 3497308 |
| Another module's implementation removed | #[RemoveHook(hook, class, method)] |
11.2.0, change record 3496786 |
| Support for core before 11.2.0 | Keep hook_module_implements_alter() and mark it #[LegacyModuleImplementsAlter] |
On 11.2+ the attribute stops the procedural function from running so the attribute ordering wins. Without the attribute the function still runs but is deprecated 11.2.0, removed 12.0.0 (change record 3496788) |
Pattern
order: on the implementation:
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Hook\Order\Order;
use Drupal\Core\Hook\Order\OrderAfter;
#[Hook('entity_presave', order: Order::Last)]
public function entityPresaveLast(EntityInterface $entity): void {}
#[Hook('form_alter', order: new OrderAfter(modules: ['field_group']))]
public function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {}
Reordering another module, the example from change record 3497308:
use Drupal\Core\Hook\Attribute\ReorderHook;
use Drupal\Core\Hook\Order\OrderBefore;
use Drupal\content_moderation\Hook\ContentModerationHooks;
#[ReorderHook('entity_presave', class: ContentModerationHooks::class, method: 'entityPresave', order: new OrderBefore(modules: ['workspaces']))]
#[Hook('entity_presave')]
public function entityPresave(EntityInterface $entity): void {}
Removing another module's implementation:
use Drupal\Core\Hook\Attribute\RemoveHook;
#[RemoveHook('form_alter', class: OtherModuleHooks::class, method: 'formAlter')]
final class MyHooks {}
The attributes take effect wherever they sit inside src/Hook/; core recommends placing #[ReorderHook] on the implementation that needs the other one moved (change record 3497308).
Common Mistakes
- Looking for a
priority:parameter → there is none. Ordering isorder:on#[Hook],#[ReorderHook],#[RemoveHook]. new OrderBefore()with no arguments →LogicException('Order must provide either modules or class-method pairs to order against.')(core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php).- Targeting a class-form implementation with
method: 'someName'→ the target method is'__invoke'when the#[Hook]sits on the class. - Ordering against a module that does not implement the hook → silent no-op.
BeforeOrAfter::apply()returns without change when the reference identifier is absent. - Assuming
Order::Firston two modules gives a defined order → both move to the front; the last applied wins. UseOrderBeforeagainst the other module instead. - Using
order:,#[ReorderHook], or#[RemoveHook]in a theme →LogicExceptionat container build. See Theme Hooks (OOP).
See Also
- ← Hook Attribute Anatomy | Procedural-Only Hooks →
- Reference:
core/lib/Drupal/Core/Hook/Order/Order.php,OrderBefore.php,OrderAfter.php,RelativeOrderBase.php;core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php,FirstOrLast.php;core/lib/Drupal/Core/Hook/Attribute/ReorderHook.php,RemoveHook.php,LegacyModuleImplementsAlter.php - Change records: https://www.drupal.org/node/3493962, https://www.drupal.org/node/3497308, https://www.drupal.org/node/3496786, https://www.drupal.org/node/3496788, https://www.drupal.org/node/3515207