To Alter or Dispatch: Drupal 8 Events versus Alter Hooks

Drupal 8 comes with two extension points for module developers to allow other modules to interact with their code.

The trusty alter hook, the linchpin of Drupal versions past is still there - allowing other modules to interact and intervene in the behaviour of your module.

But there is a new kid on the block, the event system.

So as a module developer how do you decide whether to use the alter system or the event system.

To alter or to fire an event?

This was the premise of a recent IRC conversation between myself, dawehner and bojanz, so for posterity I present to you the pros and cons we came up with.

Pros of events

  • Object oriented. Hooks still rely on procedural code meaning unit-testing is possible, but more involved. As events are object-oriented and support dependency injection, you could unit-test your event. Although in practise your events should be thin, like your controllers, with your logic deferred to the larger parts of your application. Either way, Object oriented code presents a nicer developer experience than working with procedural functions.
  • Stopping propagation. Like a JavaScript event, the event dispatcher allows any single event to stop propagation, preventing subsequent event listeners from firing. This can't be done with an alter hook.
  • Firing twice on the one event. By default with the hook system, your hook is fired based on alphabetical sorting. i.e. barfoo_some_hook will go before foobar_some_hook because barfoo comes before foobar in the alphabet. If you want to alter the order in which the hooks fire, you can alter the module's weight in the system table - which applies globally for all hooks, or implement hook_module_implements_alter to re-order on a per-hook basis. What you can't do however is fire your hook twice. With the event system, you can register two listeners for the one event, from the one module. This means you can have your event listener fire first and last when the event is dispatched.

Pros of alter hooks

  • One thing the event system doesn't easily allow is for one module to remove another module's implementation. This can be done with hook_module_implements_alter in the hook system. So for your particular site-build you may find one module's implementation of a hook problematic or even broken. With the hook system, you can implement hook_module_implements_alter and remove a particular implementation and re-implement it in your own way. To do something similar in the event-dispatcher system, would require you to alter the container definition and remove the event listener you don't want.

Code sample for hooks

<?php
function mymodule_dosomething() {
$data = mymodule_get_some_data();
return \Drupal::moduleHandler()->alter('my_module_hook', $data);
}
?>

Code sample for events

<?php
namespace Drupal\mymodule;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\mymodule\Events\SomethingEvent;
/**
 * Defines a class for doing stuff.
 */
class MyModuleSomething {
  protected $eventDispatcher;

  /**
   * Constructs a MyModuleSomething.
   */
  public function __construct(EventDispatcherInterface $event_dispatcher) {
    $this->eventDispatcher = $event_dispatcher;
  }

  /**
   * Does something.
   */
  public function doSomething($with_this) {
    $event = new SomethingEvent($with_this);
    $this->eventDispatcher->dispatch(SomethingEvent::JUST_DO_IT, $event);
    return $event->showMeTheDoneStuff();
  }
}
?>

So what will it be?

Are you building a module for Drupal 8 and have already decided to use events instead of alter hooks? Let us know what influenced your decision in the comments - let's keep the conversation going and build out a resource to help others make their decision.

Drupal 8 Event Dispatcher Event Listener Hooks
Back to top

Comments

Posted by klausi | March 18, 2015

How can a module alter the container definition to remove an event subscriber? That would eliminate the single pro of alter hooks. Or is that something the site builder/developer must du manually?

larowlan's picture
Posted by larowlan | March 18, 2015

A module can define a magically named class (yes yuck) and extend ServiceProviderBase or implement ServiceModifierInterface see https://api.drupal.org/api/drupal/core!lib!Drupal!Core!DependencyInjection!ServiceModifierInterface.php/interface/ServiceModifierInterface/8 for the interface and https://github.com/md-systems/file_entity/blob/8.x-2.x/src/FileEntityServiceProvider.php for an example

Posted by Larry Garfield | March 18, 2015

Contribution modules should use events almost exclusively.

Ok, not true. For many cases a tagged service is also a viable option. The pros and cons of those are more detailed the I can go into while typing on a phone, but hooks are right out in most cases. Their only remaining use is alter hooks, and that's only because the code for using events for that pattern is rather verbose by comparison (but entirely possible, as core is already doing it for routing).

Really, no hooks. Never define your own hooks in Drupal 8.

Posted by Chris Hall | March 18, 2015

This article should really also show the differences from the other end, is there a quick and possibly 'dirty' alternative to implementing EventSubscriberInterface to act on an event for example? To many people subscribing to events will be the main barrier, I can imagine a lot of module conversions will entail routing, and config changes, a controller and form to manage admin functionality and then attempting to cling as much as possible to the .module file because this is the only place realistically where you can get some kind of quick win conversion from D7 code.

Another useful indicator, will be what heavy hitter hooks are still in D8? hook_form_alter() for one! Is there an event I can subscribe to to implement this functionality in D8 as well? If not then the many form based modules are going to remain pretty much tied into .module and functional approach (because it will be path of least resistance). What other big, useful alter hooks are still around in D8?

These are just my first impressions, only just started looking seriously at D8 again.

Posted by Fabianx | March 18, 2015

There are performance reasons why Drupal 8 did not switch everything over to events, yet.

Yes, the event dispatcher has been replaced, but events still have deficients to hooks - else there would be no reason to ship core still with hooks.

Lets work together to bring events to the level of hooks both in terms of DX and performance and then we can remove all procedural code.

Thanks,

Fabian

Posted by EclipseGc | March 19, 2015

I think the other thing worth pointing out here is that alter hooks really anticipate arrays, but would work just as well with an array of objects. Events, by comparison really NEED an object to function properly, which in the case of something like an array of objects or multidimensional array (our typical alter hook use case) really means implementing ArrayInterface or similar around your wrapper of things that need to be alterable. Looking back on the Plugin system's creation, I wish we'd have built that functionality instead of alter hooks, but as a transitionary step, I still think we're in a really good place.

Bottom line, Event Dispatching is awesome, but definitely not as simple to implement and manipulate as our traditional alter_hooks.

Posted by Frank | March 19, 2015

Aren't your Pros contradictory?

If you can stop another module's hook from firing then you can also stop the propagation of that hook.

Hook can offer a better DX for simple tasks. They only pose a problem when they are over used or the only way to get something done. This is pretty clear from the example code that for a simple task, alter hooks work very well. OOP definitely fairs better for complex situations, but even OOP can have alters through an advise based system.

larowlan's picture
Posted by larowlan | March 19, 2015

Yes, hook_form_alter will still be there. This article is less about 'will I implement a hook or an event' and more about 'as a module developer should I fire an alter hook or dispatch an event'.

Posted by Chris Hall | March 19, 2015

I think I was indirectly addressing that, one indicator may be consistency, so if it starts of hooky then stay with the hooks, the use-case I was looking at starts with hook_form_alter to implement some functionality but then allows others to hook into that if they require (they could also be conceivably implementing hook_form_alter() themselves for something related). Having started with hooks (even if you didn't provide them) it could get very messy and confusing (and considerably more difficult I guess) to mix and match.


Posted by David Rothstein | March 19, 2015

One advantage of hooks not yet listed here is that they are better for when you need to guarantee a particular execution order. For example, if module B's hook must run after module A's, module B can easily guarantee that in hook_module_implements_alter(). With events, module B could set a lower priority than module A to force the same behavior, but it breaks as soon as module A decides to change *its* priority (for example, because it needs to run after module C). So it's considerably more fragile. (There is, however, an issue that proposes fixing this for events: https://www.drupal.org/node/2352351)

Also:

"What you can't do however is fire your hook twice."

Actually, I think you can... if you're adventurous :) There's nothing that requires hook implementations added via hook_module_implements_alter() to have a one-to-one correspondence with modules. So if your module is named 'mymodule' and you put this code in your hook_module_implements_alter() implementation:

if ($hook == 'form_alter') {
$implementations['mymodule_2'] = FALSE;
}

Then you can define the function mymodule_2_form_alter() along with the regular mymodule_form_alter(), and both will be called.

Is that recommended? - no, probably not. But it should work.

larowlan's picture
Posted by larowlan | March 19, 2015

sneaky, sounds like that'd be fun to try and debug ;)

Posted by Bret | March 29, 2016

Hi! And thanks for the comprehensive tutorial. Drupal 8 is kinda tricky. I'd say that altering data is a good choice. A quick description here if you need a briefer one: http://westweb-solutions.com/blog/drupal-8-allow-other-modules-alter-your-data

Post a comment