Allow modules to register services and subscriber services (events)

Created on 24 May 2012, about 12 years ago
Updated 21 July 2023, 11 months ago

Problem/Motivation

Currently, modules cannot register new services in the DIC. They also cannot register their own event subscribers. That was acceptable for the kernel patch to get it in place, but we cannot actually call the system working if it's not extensible. Modules need a low-cost way to register services and event listeners/subscribers.

Proposed resolution

We're going to steer as close to Symfony as possible here. To that end, what we need to do is:

- Switch from using EventDispatcher to using ContainerAwareEventDispatcher.
- Allow classes to provide a class implementing HttpKernel\BundleInterface. Bundles provide the touch point for "compiler passes" for the dependency injection container, which allow any bundle to add things to the DIC.
- Because later compiler passes can add information to previous passes, one of the things you can do there is add "extenders" to the dispatcher entry in the DIC. That is, "when you're doing the setup and instantiation for this object, do this stuff, too." "This stuff" is then registering additional subscribers out of the DIC.
- Modules create a service for each subscriber they are registering, then add that service as a subscriber to the dispatcher.

As it stands in 8.x we are using the HttpKernel class from Symfony, whose task is to handle the request and return a response. In this patch we subclass Symfony's Kernel class, which does a lot more besides handling the request and response (internally it uses an HttpKernel to do that part). It actually creates the dependency injection container and registers Bundle classes so that those bundles can then add services to the DIC. The patch provides a CoreBundle, which extends Symfony's Bundle class, and this registers all of the services that the core subsystems need. They get added in the bundle's build() method. Any services that need to subscribe to events are tagged as such, e.g.:

$container->register('router_listener', 'Drupal\Core\EventSubscriber\RouterListener')
  ->addArgument(new Reference('matcher'))
  ->addTag('kernel.event_subscriber');

The CoreBundle adds a compiler pass, RegisterKernelListenersPass, that goes through all services tagged with 'kernel.event_subscriber' and adds them as subscribers to the dispatcher service.

Any module wishing to get in on this action would provide its own MyModuleBundle class, subsclassing the Symfony Bundle class directly, not CoreBundle. In the build() method it would register services to the DIC, adding the 'kernel.event_subscriber' tag to any that need to be added as subscribers to the dispatcher.

Another significant change added in this patch is that the Request object is now handled in the DIC. This means we can have services in the DIC that depend on the Request, the one current example of this being the new LanguageManager class that returns a language object depending on type, e.g. LANGUAGE_TYPE_INTERFACE. This means that the actual language negotiation logic can use information from the request directly, like the path, to determine the language.

The patch removes the request() wrapper function.

Remaining tasks

Nail the one outstanding test failure.

Ensure sufficient test coverage for the new DrupalKernel/DIC/CoreBundle setup.

After this, actually compiling the DIC so that we don't have to reinitialize it every request load will become even more critical.

User interface changes

None, this is all code-level stuff.

API changes

All modules have (or can have, TBD) a bundle class that acts as a sort of info-hook-ish tool to add anything to the DIC.

Original report by Crell

Right now, the kernel has a whole bunch of hard-coded subscribers. That's well and good for now, but we need to allow modules to add their own listeners, too. And it needs to be done in a performant fashion.

The goal here is to allow any module to:

1) Add subscribers to a common EventDispatcher object.
2) Subscriber objects should NOT have to all be instantiated in advance. That would not be performant.
3) Subscriber objects should accept dependency objects, so they can be put into the DIC.
4) This process may *not* use hooks, because that binds two event systems together too tightly and hooks are not available until full bootstrap.

I don't know at the moment how best to do this, but it is a requirement for Drupal. I know that Symfony full stack has a solution points 2 and 3, but I don't know how it works yet. That's where we should start investigating first.

πŸ“Œ Task
Status

Fixed

Version

8.0 ⚰️

Component
BaseΒ  β†’

Last updated about 3 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States Crell

Live updates comments and jobs are added and updated live.
  • D8MI

    (Drupal 8 Multilingual Initiative) is the tag used by the multilingual initiative to mark core issues (and some contributed module issues). For versions other than Drupal 8, use the i18n (Internationalization) tag on issues which involve or affect multilingual / multinational support. That is preferred over Translation.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024