All hook invocations have been been delegated as a responsibility of Module Handler service. Modules must no longer construct hook functions on their own. This will allow future improvement to the hook system since hooks/alters are fully centralized.
\Drupal\Core\Extension\ModuleHandlerInterface::getImplementations() and \Drupal\Core\Extension\ModuleHandlerInterface::implementsHook() has been deprecated. If you needed this for custom hook invocations, use Drupal\Core\Extension\ModuleHandlerInterface::invoke*With() methods instead.
The interface for module handler service has changed. \Drupal\Core\Extension\ModuleHandlerInterface introduces:
public function invokeAllWith(string $hook, callable $callback): voidpublic function hasImplementations(string $hook, $modules = NULL): bool
The hook invoker will no longer have any insight into the implementation of a hook, such as the function name, but will know the module name where it is defined. Thusly callers must not rely on the fact an implementation will be called, or that there will only be one implementation per module.
Responsibility for iteration has been moved from invokers to the module handler service. Invokers are now given the opportunity to pass a closure which will be invoked for each hook implementation. Each closure is passed a callable and the module name. The callable must be called to trigger the hook implementation.
All existing behavior can be replicated by migrating to \Drupal\Core\Extension\ModuleHandlerInterface::invoke*With() methods with minor changes to logic.
Examples
Old implementation
$hook = 'myhook';
foreach ($this->moduleHandler->getImplementations($hook) as $module) {
$this->moduleHandler->invoke($module, $hook);
// Or..
$function = $module . '_' . $hook;
$function(...$args);
}
New implementation
- Change module handler method from
getImplementationstoinvokeAllWith. - Remove
foreachloops, instead surrounding existing logic with a closure. - Import dependent data with
use. - Invoke hooks with
$hook(). You may pass existing arguments to it as normal, return values also work the same.
$hook = 'myhook';
// Simple:
$this->moduleHandler->invokeAllWith($hook, function (callable $hook, string $module) {
$hook();
});
// Advanced, if you need to import data or juggle state:
$results = [];
$this->moduleHandler->invokeAllWith($hook, function (callable $hook, string $module) use (&$results) {
$results[$module] = $hook();
});
// Getting a list of hook implementors is as simple as making use of the $module variable.
$implementors = [];
$this->moduleHandler->invokeAllWith($hook, function (callable $hook, string $module) use (&$implementors) {
// There is minimal overhead since the hook is not invoked.
$implementors[] = $module;
});
Some or all invocations of the following hooks in core have been switched to use invokeAllWith
hook_cronhook_entity_base_field_infohook_entity_bundle_field_infohook_entity_field_accesshook_entity_field_accesshook_entity_field_storage_infohook_entity_loadhook_ENTITY_TYPE_loadhook_entity_storage_loadhook_ENTITY_TYPE_storage_loadhook_entity_type_buildhook_field_formatter_third_party_settings_formhook_field_widget_third_party_settings_formhook_helphook_js_settings_buildhook_jsonapi_ENTITY_TYPE_filter_accesshook_jsonapi_entity_filter_accesshook_mailhook_node_grantshook_page_attachmentshook_page_bottomhook_page_tophook_rdf_namespaceshook_search_preprocesshook_update_last_removedhook_views_data