Earlier Drupal versions have a hook system, a way of letting other developers write code that interacts with core code in a clean and organized manner. System events are defined, and when they occur, the entire application is notified, and all the code associated with the event is run.

The hook system is based on the naming and definition of functions. When certain events occur, e.g. a form has been built, a node is being inserted into the database, a user has just logged in, etc., Drupal asks all modules a question like, "Anything else?" or "What would you like to do now?". Drupal does this by looking for functions matching a particular pattern. It then runs all matched functions. Core and custom code are written in uniquely-named modules, so that is the ideal path to follow in order to identify any special functions. They are named as MODULE_HOOK. For example, to invoke a user_login hook in login_example module, Drupal will look for a function called login_example_user_login().

The EventDispatcher component of Symfony takes an Object-Oriented Programming (OOP) approach to having components of an application communicate with each other by dispatching events and listening to them.

Kernel Events

The HttpKernel comes with the following key events, represented by the following constants:

// vendor/symfony/http-kernel/KernelEvents.php

// when a request is beginning to be dispatched 
// when an uncaught exception appears
// when a controller has returned anything that is not a Response
// when a controller has been found for handling a request
// when a response has been created for replying to a request 
// when a response has been sent
// when a response has been generated for a request

Core Events

Drupal core builds upon the EventDispatcher component by defining more events, and the constants representing them are as follows:

// core/lib/Drupal/Core/Config/ConfigEvents.php
// when information on all config collections is collected
// when deleting a configuration object
// when importing configuration to target storage
// when validating imported configuration
// when renaming a configuration object
// when saving a configuration object

// core/lib/Drupal/Core/Entity/EntityTypeEvents.php
// when a new entity type is created
// when an existing entity type is deleted
// when an existing entity type is updated

// core/lib/Drupal/Core/Field/FieldStorageDefinitionEvents.php
// when a field storage definition is created
// when a field storage definition is deleted
// when a field storage definition is updated

// core/modules/language/src/Config/LanguageConfigOverrideEvents.php
// when deleting a configuration override
// when saving a configuration override 

// core/modules/locale/src/LocaleEvents.php
// when saving a translated string

// core/modules/migrate/src/Event/MigrateEvents.php
// when saving a message to the idmap
// when removing an entry from a migration's map
// when saving to a migration's map	
// when finishing a migration import operation
// when finishing a migration rollback operation
// when a single item has been deleted
// when a single item has been imported
// when beginning a migration import operation
// when beginning a migration rollback operation
// when about to delete a single item
// when about to import a single item

// core/lib/Drupal/Core/Render/RenderEvents.php
// when selecting a page display variant

// core/lib/Drupal/Core/Routing/RoutingEvents.php
// when collecting routes to allow changes to them
// when collecting routes to allow to allow new ones
// when route building has finished

Other Events

There may be other events from contributed modules or vendor packages, such as ConsoleEvents in vendor/symfony/console/ConsoleEvents.php. Custom modules can also define new events.

Subscribing to Events

To hook into the events system of the EventDispatcher component, a subscriber class needs to:

  • implement the EventSubscriberInterface
  • implement getSubscribedEvents() method and return an array keyed by the event to listen to and one of the following as value:
    • the name of a method to call (the priority will default to 0)
    • an array of a method name to call and prioritize
    • an array of arrays comprising the method names and respective priorities (the default priority is 0, if left out)

To inform Drupal that we have subscribed to events, we need to add a services configuration file (MY_MODULE.services.yml) to our module.

Let us go through an example in core/modules/user/src/EventSubscriber/UserRequestSubscriber.php. If a user is logged in, every time a request has ended, the last access time is logged against the user's account. Also, we do not want to do this too frequently so we opt for not more than once every 180 seconds.

Step 1: We subscribe to the KernelEvents::TERMINATE event.

public static function getSubscribedEvents() {
    $events[KernelEvents::TERMINATE][] = ['onKernelTerminate', 300];
    return $events;

The service id is KernelEvents::TERMINATE, the method we want to call is onKernelTerminate and the priority is 300.

Step 2: The onKernelTerminate() method needs to be implemented:

public function onKernelTerminate(PostResponseEvent $event) {
    if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
      $storage = $this->entityManager->getStorage('user');
      $storage->updateLastAccessTimestamp($this->account, REQUEST_TIME);

Here, the EntityManager allows us to save the data easily.

Step 3: The configuration is in user.services.yml:

    class: Drupal\user\EventSubscriber\UserRequestSubscriber
    arguments: ['@current_user', '@entity.manager']
      - { name: event_subscriber }

@current_user and @entity.manager are services too.


This was an overview of the implementation of the EventDispatcher component in Drupal 8. There are lots of events to subscribe to, and if necessary, you can create your own. Let us save that for our next short article on creating and subscribing to events.

Recommended reading

  1. Events and Event Listeners
  2. Events