Problem/Motivation

Hooks are invoked and events dispatched using an arbitrary string. Hook implementations and event listeners then get picked up based on that arbitrary string.

However, with events, we've adopted the pattern of putting the string in a constant on an Events class. See for example https://api.drupal.org/api/drupal/core!modules!migrate!src!Event!Migrate...

This has caused problems on module enable, see #2712647: Update Symfony components to ~3.2 and #2776235: Cached autoloader misses cause failures when missed class becomes available.

Event listeners reference the event class in getSubscribedEvents() - but they either have to rely on the event class always being loaded, or use class_exists() to check if it's available. This introduces a very fragile dependency on the classloader being updated when the module list is.

Proposed resolution

Stop using event classes with constants for event names, just use strings everywhere.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

catch created an issue. See original summary.

dawehner’s picture

Using constants had kind of an advantage of making it easier to search for usages. On the other hand, having issues with autoloadable code is a much worse problem to be fair.

+1 for doing so. When we do so we should

  • Document our reasing
  • Keep the constants around though for documentation purposes
  • Convert all the instances using a script
Crell’s picture

Would it make sense to keep them at the dispatch point, even if we remove them from the registration point? Or would that have the same potential autoloader fail?

dawehner’s picture

Would it make sense to keep them at the dispatch point, even if we remove them from the registration point?

IMHO this should totally work. On the dispatcher level you need to have the event classes available as well, so code would fail, when the module namespaces cannot be loaded.

On the other hand this would be a bit non ideal, as it makes it less easy to find implementors and dispatchers of a certain event.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

fgm’s picture

One thing I did when I had to implement a custom event dispatcher / emitter for D7 (for Heisencache) was the ability, just like with the NodeJS EventEmitter, to emit just about any string as an event. To keep discoverability, I added an EventEmitterInterface which enabled declaring emitted events.

In D8, a similar mechanism would allow event discovery: it could be a service tag (but of course that would mandate that emitters be services, which is not necessarily the case), or use a discovery compiler pass, like we do to discover serviceproviders: we could have eventproviders in the same vein.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

andypost’s picture

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

andypost’s picture

Version: 8.9.x-dev » 9.0.x-dev
andypost’s picture

it depends on used symfony version

xjm’s picture

Version: 9.0.x-dev » 9.1.x-dev

9.0.x should only contain major-only changes, or bugs that only affect 9.0.x+. Feature requests should be filed against 8.9.x and will be shortly updated to 9.1.x.

Since this apparently requires Symfony 4 (if I understand #16), moving to 9.1.x.

catch’s picture

#3055194: [Symfony 5] The signature of the "Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3. moves to not requiring an event name at all, and using the FQDN of the event class instead.

This shows that Symfony is moving away from event names altogether, but it does not necessarily change the decision we have to make here (especially since we have 1-N relationships with some events).

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

catch’s picture

Title: Stop using event class constants » Event class constants for event names can cause fatal errors installing from configuration
Category: Task » Bug report
Priority: Normal » Critical
catch’s picture

Priority: Critical » Major

There is a workaround for contrib that's documented in the issue summary here - modules need to check class_exists() before subscribing to an event via one of these constants.

However it's definitely a bad pattern, splitting the difference at major.

mkalkbrenner’s picture

The issue not just happens when installing from existing config. it also happens when just installing a module in the standard way when the constant is defined in a dependency that is not yet installed. That's why I choose "critical".

catch’s picture

Title: Event class constants for event names can cause fatal errors installing from configuration » Event class constants for event names can cause fatal errors when a module is installed

#22 there's still a possible fix in the contrib module for that case too in that they can use class_exists(), which is why I'd suggest this is major - either way it's a real problem that we should fix. And just moving away from the class constants shouldn't be that difficult really.

berdir’s picture

The thing is that Symfony is moving in the opposite direction, to use the event class name instead of a separate constant, so that a bit more awkward to hardcode as a string. But this issue feels to a certain point by-design, at least for optional integrations.

catch’s picture

That's true but our event class constants aren't even on the event class, they're in weird classes all to themselves that just hold constants for event names (which I never understood the point of). https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Config%21...

It should be fine to use an event class and use the class name without a class_exists, since the class doesn't need to be loaded to use the event name as a string. So going in Symfony's direction ought to help here too.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

fabianx’s picture

#25 They are in extra classes, because that’s a Symfonyism.

I agree that it makes much more sense to use:

::class if there is only one event and maybe have an optimized class to subclass with a listen method that is automatically registered to reduce boilerplate?

And $classEvent::EVENT_1, $classEvent::EVENT_2 when there is more than one thing using the event (though the original pattern might have been there for discoverability) OR just create a base class and use ::class anyway.

eg workspace\PrePublishEvent extends WorkspaceEvent.

Which is probably cleaner anyway.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

longwave’s picture

I run into this time and time again on customer projects where we add a new contrib module and an event subscriber for that contrib to an existing custom module, and then get fatal errors during deployment when the module isn't enabled yet.

Big +1 for moving to event class names directly, and subclassing as in #28 for the case where we have similar events.

longwave’s picture

Symfony supports event name aliases so we should be able to make this change in a backward compatible way.

https://symfony.com/doc/current/event_dispatcher.html#event-aliases

We would have to add AddEventAliasesPass for core events and also provide a mechanism to allow contrib/custom to add their own event aliases.

claudiu.cristea’s picture

And $classEvent::EVENT_1, $classEvent::EVENT_2 when there is more than one thing using the event (though the original pattern might have been there for discoverability) OR just create a base class and use ::class anyway.

I think we should avoid using an event class for many events. Each event should have its own class, even that would need a base class or a trait to handle code reusability

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.