Allow to skip OOP hooks and services for modules that are not installed

Created on 14 May 2025, about 23 hours ago

Problem/Motivation

Modules often implement hooks of other modules they don't depend on as optional integrations. That works basically out of the box with legacy hooks and also new OOP hooks. But they are still registered and their class added to the container when we know they will never be called. For example, user module implements views hooks.

This becomes a bit more complicated if your hook also requires an injected dependency of that module. Because the class is added as a service, it will be invoked and you need to make that dependency optional even though that class and hook actually requires it. And we'll run into that more often when improving Hooks to do proper DI.

And, unlike in old version where hooks were only discovered and cached when invoked, now we add them all to the container.

If you provide a plugin for a given plugin type owned by another module that isn't installed this works as it's never discovered.

Steps to reproduce

Proposed resolution

We can't figure this out automatically, we don't have enough knowledge about hooks and who owns them.

My idea was to make it explicit with a new attribute:

#[Hook('config_translation_info_alter')]
#[HookBy(module: 'config_translation')
public function configTranslationInfoAlter(array &$info): void {

We can check for that, and if config_translation isn't in the container, we skip it. And if a service class has no hooks, we also don't add it to the container.

It might also automatically work if the hook class is defined by that module, like this:

#[ConfigTranslationInfoAlter('config_translation_info_alter')]
public function configTranslationInfoAlter(array &$info): void {

If we can't resolve ConfigTranslationInfoAlter as an attribute class. But I'm not sure yet if we're really going to add hook classes for every hook.

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Feature request
Status

Active

Version

11.2 🔥

Component

base system

Created by

🇨🇭Switzerland berdir Switzerland

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @berdir
  • 🇧🇪Belgium kristiaanvandeneynde Antwerp, Belgium

    Declaring all your hooks as extensions of Hook would definitely solve this as we can then support a "source" property on the base Hook class that we can scan for as described in the IS. The only drawback I see is we'd be creating tons of one-of attributes. They would be contained to namespaces that have Hook in them, so I suppose that could be fine.

    So maybe add an optional "source" property to Hook that we can specify in the Hook constructor as a named argument and then extensions of Hook can prefill it for you? The default value could be empty or "drupal" to indicate that it's a core hook.

    Anyways, big +1 to the idea. We shouldn't add classes to the container if we know they will never be called.

Production build 0.71.5 2024