Skip to main content
Lorsque vous souhaitez doter votre package de points d’extension pilotés par les événements, concevez séparément la fourniture des classes d’événements et l’enregistrement des écouteurs. Une configuration qui fonctionne sans que les utilisateurs aient à ajouter un répertoire de vendor à la découverte automatique réduit les hypothèses à respecter lors de l’installation, de la mise à jour et de la création du cache. Cette page examine l’implémentation de Laravel Framework v13.36.0 et applique les opérations de base des événements à la maintenance à long terme d’un package. Pour le chargement du provider, consultez Découverte automatique des packages, et pour l’environnement de test, consultez Tester des packages.

Enregistrer explicitement les écouteurs du package

Les traitements que le package exécute par défaut peuvent être enregistrés avec Event::listen() dans la méthode boot() d’un Illuminate\Support\ServiceProvider ordinaire. L’emplacement de découverte automatique des écouteurs par défaut de Laravel est le répertoire app/Listeners de l’application ; le framework ne parcourt pas automatiquement le répertoire src/Listeners d’un package. À titre d’exemple, créons un package qui journalise un événement de livraison terminée. L’événement est une simple classe contenant les valeurs nécessaires à la notification.
L’écouteur reçoit un logger dans son constructeur et traite l’événement dans handle().
Dans le provider, enregistrez les noms de classes de l’événement et de l’écouteur.
Cette configuration suppose que le provider lui-même est chargé. Prévoyez extra.laravel.providers dans Composer ou un enregistrement explicite par l’utilisateur. Si l’événement est nécessaire aussi bien lors du traitement HTTP que dans les workers de file d’attente, ne restreignez pas l’enregistrement avec runningInConsole().
N’implémentez pas DeferrableProvider sur ce provider uniquement pour l’enregistrement des écouteurs. Si les services différés ne sont jamais résolus, les écouteurs ne sont pas non plus enregistrés. Si nécessaire, séparez les liaisons de services lourds dans un autre provider différé.

L’enregistrement et l’instanciation ont lieu à des moments distincts

Dispatcher::listen() ajoute l’écouteur à un tableau interne pour un nom d’événement ordinaire. Lorsque vous passez un nom de classe comme ci-dessus, l’instance de l’écouteur n’est pas créée à ce stade. Pour les écouteurs synchrones, createClassCallable() résout la classe via make() du conteneur après le déclenchement de l’événement. Vous pouvez donc utiliser l’injection de dépendances dans le constructeur. Ce chemin n’effectue pas de call() du conteneur sur handle(). Concevez l’écouteur de sorte que les services arbitraires soient injectés dans le constructeur et que handle() reçoive l’événement. Ne considérez pas ce mécanisme comme identique à l’injection de dépendances dans handle() des commandes Artisan. Il est ici question des écouteurs synchrones. Si vous implémentez ShouldQueue, un chemin distinct propre aux files d’attente s’applique : consultez également la mise en file d’attente des événements et Files d’attente concernant les transactions, les nouvelles tentatives et la résolution des dépendances.

event:cache n’empêche pas l’enregistrement explicite

event:cache n’est pas une commande qui sérialise intégralement tous les écouteurs enregistrés auprès du Dispatcher d’événements. EventCacheCommand::getEvents() énumère Illuminate\Foundation\Support\Providers\EventServiceProvider et ses providers dérivés, puis enregistre les résultats de la découverte automatique et le mappage listens() de chaque provider. Les appels à Event::listen() écrits dans un ServiceProvider::boot() ordinaire ne font pas partie de cette collecte. En revanche, même lors d’un démarrage avec cache, la méthode boot() des providers ordinaires est exécutée. Ne pas figurer dans le cache et ne pas être enregistré lorsque le cache existe sont deux choses différentes. Un package qui hérite de l’EventServiceProvider de Foundation peut aussi utiliser $listen. Toutefois, dans l’implémentation examinée, la valeur par défaut de shouldDiscoverEvents() est évaluée pour la classe de base elle-même. Le simple fait d’hériter ne déclenche pas la découverte automatique du répertoire des écouteurs du package. Par ailleurs, $subscribe et $observers de ce même provider sont traités séparément de la boucle qui enregistre les résultats de getEvents(). Ne concluez pas, en vous fondant uniquement sur le cache de $listen, que ces enregistrements sont également omis.
Event::subscribe(SomeSubscriber::class) crée le subscriber via le conteneur au moment de l’enregistrement et appelle subscribe(). Il ne bénéficie pas de la même évaluation différée que Event::listen() avec un nom de classe. Évitez les effets de bord, comme les communications externes, dans le constructeur du subscriber et dans subscribe().

Éviter le double enregistrement avec la découverte automatique

Si l’utilisateur ajoute le répertoire des écouteurs du package via withEvents(discover: [...]) et que le provider du package enregistre aussi explicitement les mêmes écouteurs, le traitement peut s’exécuter plusieurs fois pour un seul événement. Dispatcher::listen() ajoute l’écouteur au tableau pour les événements ordinaires, et le Dispatcher ne déduplique pas automatiquement les noms de classes identiques à l’échelle globale. L’array_unique() effectué par l’EventServiceProvider de Foundation au sein de son propre mappage ne fusionne pas non plus les enregistrements explicites effectués par d’autres providers. Attribuez la responsabilité de l’enregistrement à un seul côté.
  • Si le package enregistre les écouteurs par défaut, n’exigez pas de l’utilisateur qu’il ajoute ce répertoire à la découverte automatique.
  • Si l’utilisateur choisit les écouteurs nécessaires, le package fournit les classes d’événements et d’écouteurs, mais n’enregistre pas ces mêmes écouteurs par défaut.
  • Lors d’une migration de la découverte automatique vers l’enregistrement explicite, indiquez que la configuration de découverte côté utilisateur et l’ancien cache d’événements doivent également être mis à jour.
L’idempotence du traitement métier est également importante, mais ne masquez pas un double enregistrement par la seule idempotence : clarifiez plutôt les chemins d’enregistrement.

Vérifier le comportement avant et après une mise à jour

Pour examiner l’état de l’enregistrement, exécutez event:list dans une application de vérification où le package est installé. Vérifiez que l’écouteur concerné est présent, avec et sans cache.
Il s’agit d’une procédure pour une application de vérification. L’affichage de event:list ne garantit à lui seul ni que l’écouteur termine son traitement, ni qu’il ne s’exécute qu’une seule fois. Ajoutez également des tests qui déclenchent réellement l’événement. À titre d’exemple, après avoir enregistré CourierServiceProvider dans le TestCase de base de Testbench, vérifiez que le logger n’est appelé qu’une seule fois.
Event::fake() est pratique pour vérifier le déclenchement d’un événement, mais il empêche normalement l’exécution des vrais écouteurs. Ce test vérifiant l’enregistrement et le traitement, il ne l’utilise pas. Vérifiez également la combinaison avec la découverte automatique et le démarrage avec cache dans une autre application de vérification, avec une nouvelle instance d’application.
Exécuter event:cache après le démarrage ne reconstruit pas à partir du cache le Dispatcher déjà enregistré dans ce processus. Vérifiez le comportement avec cache lors d’un nouveau démarrage. Pour que les écouteurs mis à jour soient pris en compte par des workers de longue durée, un redémarrage conforme à votre procédure de déploiement habituelle est également nécessaire.

Liste de contrôle pour la maintenance à long terme

  • Documentez les noms des classes d’événements, leurs propriétés publiques et la politique d’enregistrement des écouteurs comme un contrat destiné aux utilisateurs.
  • Lorsque vous modifiez le nom ou la charge utile d’un événement auquel les utilisateurs s’abonnent, communiquez le changement conformément à la gestion de la compatibilité des versions.
  • Pour les mises à jour qui modifient la découverte automatique ou $listen, incluez la régénération de event:cache dans la procédure de déploiement.
  • Distinguez les écouteurs par défaut du package des écouteurs personnalisés de l’utilisateur, et ne faites pas enregistrer deux fois le même écouteur par défaut.
  • Maintenez, pour chaque version de PHP et de Laravel prise en charge, des tests vérifiant que l’écouteur ne s’exécute qu’une seule fois.

Sources primaires consultées

Dernière modification le 13 octobre 2026