Drupal 10 : Hooks et événements

Se familiariser avec le concept d'événements.

Cet article a été initialement rédigé pour Drupal 8 mais son contenu est toujours d'actualité pour Drupal 9 et Drupal 10.

N'hésitez pas à nous contacter ou à vous inscrire à notre formation «Drupal pour les développeurs et développeuses» pour en savoir plus !

L’une des forces de Drupal depuis sa création est la facilité d’utilisation de son extensibilité. Autrement dit, ses hooks. Implémenter un hook est très simple, il suffit de respecter une convention de nommage.

Si on regarde sous le capot, voici comment se fait l’invocation de ces hooks :

# Cron.php
class Cron implements CronInterface {
 protected $moduleHandler; // The module handler service.
 /**
  * Invokes any cron handlers implementing hook_cron.
  */
 protected function invokeCronHandlers() {
   // Iterate through the modules calling their cron handlers (if any):
   foreach ($this->moduleHandler->getImplementations('cron') as $module) {
     // Do not let an exception thrown by one module disturb another.
     try {
       $this->moduleHandler->invoke($module, 'cron');
     }
     catch (\Exception $e) {
       watchdog_exception('cron', $e);
     }
   }
 }
}

C'est la ligne $this->moduleHandler->invoke($module, 'cron'); qui fait le gros du travail. Les principales fonctions d'invocation des hooks ou de modification de données (drupal_alter()) sont pilotées par la classe ModuleHandler. Pour les utiliser il faudra passer par le Service module_handler et appeler la méthode dont vous avez besoin. Si vous ne vous rappelez plus ce qu'est un Service, je vous invite à lire le billet sur Conteneur de Services et Services.

Beaucoup de hooks historiques ont été remplacés par des événements Symfony mais tous les hooks ne sont pas morts. Le motif des hooks est toujours très utile pour modifier les déclarations comme les formulaires via hook_form_FORM_ID_alter(), ou les données passées à une clé de thème par exemple avec hook_preprocess_HOOK().

Dans le chapitre dédié aux annotations nous verrons qu’il y a d’autres mécanismes qui remplacent également les hook_*_info().

Hooks VS Events

Le principe des hooks peut être implémenté en suivant plusieurs design patterns différents, seulement voilà, Symfony vient avec sa propre implémentation grâce à sa classe EventDispatcher. Lorsque l’on souhaite connaître tous les modules qui implémentent un hook, on déclenche (dispatch) un événement. Chaque module qui implémente ce hook se signale en souscrivant (subscribe) à cet événement et retourne les données appropriées.

On retrouve par exemple l’utilisation des événements dans la gestion des routes pour afficher une réponse HTML.

# core.services.yml
html_response.subscriber:
 class: Drupal\Core\EventSubscriber\HtmlResponseSubscriber
 tags:
   - { name: event_subscriber }
# HtmlResponseSubscriber.php
class HtmlResponseSubscriber implements EventSubscriberInterface {
 // The HTML response attachments processor service.
 protected $htmlResponseAttachmentsProcessor;

 public function __construct(AttachmentsResponseProcessorInterface $html_response_attachments_processor) {
   $this->htmlResponseAttachmentsProcessor = $html_response_attachments_processor;
 }

 public static function getSubscribedEvents() {
   // Appeler la méthode onRespond lorsque l'événement KernelEvents::RESPONSE survient.
   $events[KernelEvents::RESPONSE][] = ['onRespond'];
   return $events;
 }

 /**
  * Processes attachments for HtmlResponse responses.
  */
 public function onRespond(FilterResponseEvent $event) {
   if (!$event->isMasterRequest()) {
     return;
   }

   $response = $event->getResponse();
   if (!$response instanceof HtmlResponse) {
     return;
   }
   $event->setResponse($this->htmlResponseAttachmentsProcessor->processAttachments($response));
 }
}

On déclare un Service qui implémente l’interface EventSubscriberInterface, on y implémente la méthode getSubscribedEvents() pour indiquer à quel(s) événement(s) réagir et quelle fonction appeler lorsque l’événement survient. L’appel à ce Service se fait automagiquement uniquement si on l’a tagué comme event_subscriber.

Illustration dans le style de Batman de l'invocation d'un événement auprès des écouteurs.

Votre commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Votre adresse servira à afficher un Gravatar et à vous notifier des réponses. Votre commentaire sera anonymisé si ce billet est dépublié pendant plus de 3 mois.
Pour lutter contre le spam notre système enregistre votre adresse IP et votre adresse e-mail si vous la partagez.
Nous vous invitons à consulter notre politique de confidentialité pour comprendre les traitements faits de ces données et comment les rectifier.

À propos de Julien

Co-fondateur - Scrum master & Expert technique

Utilisateur de Drupal depuis 2008, j’ai fait mes armes comme développeur chez Commerce Guys puis me suis mis à encadrer les nouveaux arrivants avant de donner des formations, participer aux avant ventes et accompagner les équipes au passage à Scrum.

Je suis impliqué dans la communauté française de Drupal depuis 2009, j’ai été tour à tour président puis vice-président de l’association Drupal France et francophonie entre 2011 et 2013.