Hook system

The hook system is Drupal core's machinery for hooks: it collects implementations when the container is built and runs them through the module handler.

Learn DrupalDrupal glossary

The hook system is the mechanism in Drupal core that lets modules react to events and change data without editing each other's code. Code announces a hook by name, and the module handler service finds the implementations in every enabled module and calls them one after another. Core modules, contributed modules and custom modules all extend Drupal this way.

How the hook system works in Drupal 11

Three parts work together. A definition names the hook and documents its parameters and return value, normally in a *.api.php file that Drupal never loads. An implementation is the code a module supplies. An invocation is the call that triggers it, made through the module_handler service or \Drupal::moduleHandler().

The three calls you meet most are these:

  • invoke() calls the hook in one named module.
  • invokeAll() calls every implementation and merges the arrays they return.
  • alter() hands a variable to every hook_TYPE_alter() implementation by reference, so each module can change it. The type can be a list, such as a generic form alter followed by a form-specific one.

In Drupal 11.4, a compiler pass named HookCollectorPass finds implementations while the service container is built. It looks at classes in the Drupal\my_module\Hook namespace that carry the #[Hook] attribute, registers them as autowired services, and also records procedural functions.

Implementations run in order of module weight, then alphabetically by module name. Drupal 11.2 added an order argument on #[Hook], and the #[ReorderHook] and #[RemoveHook] attributes change or skip another module's implementation.

Limits worth knowing

Install and update hooks such as hook_install(), hook_schema() and hook_update_N() must stay procedural, as must the legacy hook_hook_info() and hook_module_implements_alter().

Hooks are not the only extension route. Drupal also dispatches Symfony events, which a service tagged event_subscriber can answer.

Example

A module that builds a report can let other modules change its rows. It documents hook_my_module_report_alter() in my_module.api.php and invokes it:

\Drupal::moduleHandler()->alter('my_module_report', $rows, $context);

Another module then implements it:

#[Hook('my_module_report_alter')]
        public function reportAlter(array &$rows, array $context): void {
          // Change $rows here.
        }

The attribute takes the hook name without the hook_ prefix.

Common mistakes

  • Skipping the cache rebuild. Implementations are collected when the container is built, so a new class is not called until the container is rebuilt. A full cache rebuild does that.
  • Relying on order by accident. If your result depends on running before or after another module, declare it with the order argument. Core calls hook_module_implements_alter() a legacy hook and documents order and #[ReorderHook] for ordering.

For marketing and content teams

Every enabled module can answer the same hook, so one visible behavior, such as a form field, an email or a publishing rule, can be changed in several places. When something behaves differently from what the settings say, ask your developer which hook implementations touch it. Behavior added in a custom module through a hook should be documented, because it does not appear in any settings page.

Sources

Questions about Hook system

A hook is one named extension point, such as hook_form_alter. The hook system is the machinery around all of them: finding implementations, ordering them and calling them through the module handler service.

Yes. With class methods, one module can answer a hook in several classes, which is handy for hook_form_alter(). Four hooks are the exception and allow one implementation per module: hook_help(), hook_mail(), hook_library_info_build() and hook_node_update_index().

Not in Drupal 11. The change record for Drupal 11.1 says core expects to remove support for procedural hooks in a later version, Drupal 13 at the earliest. Class methods with #[Hook] are the preferred style.

Related terms

All Drupal terms from A to Z