Programmation PHP/Symfony/Évènement


Un évènement est une action pouvant en déclencher d'autres qui l'attendaient, à la manière du patron de conception observateur, via un hook.


InstallationModifier

Terminal
 
composer require symfony/event-dispatcher


CommandeModifier

Pour lister les évènements et écouteurs d'un projet (avec leurs priorités) :

 php bin/console debug:event-dispatcher

Ex :

"console.terminate" event
-------------------------

 ------- ----------------------------------------------------------------------------- ----------
  Order   Callable                                                                      Priority
 ------- ----------------------------------------------------------------------------- ----------
  #1      Symfony\Component\Console\EventListener\ErrorListener::onConsoleTerminate()   -128
  #2      Symfony\Bridge\Monolog\Handler\ConsoleHandler::onTerminate()                  -255
 ------- ----------------------------------------------------------------------------- ----------


EventModifier

Pour utiliser ce système, la première étape consiste à déterminer si on souhaite utiliser un évènement existant, ou en créer un nouveau.

  • Pour un existant, son nom est obtenu par le commande ci-dessus.
  • Pour un nouveau, voici un exemple de conception pilotée par le domaine où l'on souhaite qu'une condition du core soit traitée dans des modules en fonction du groupe utilisateur, sans les lister dans le core :
class AddExtraDataEvent
{
    /** @var string */
    private $userGroup;

    public function __construct(string $userGroup)
    {
        $this->userGroup = $userGroup;
    }

    public function getUserGroup(): string
    {
        return $this->usernGroup;
    }

    public function setUserGroup(string $usernGroup): AddExtraDataEvent
    {
        $this->userGroup = $userGroup;

        return $this;
    }
}

Une fois la classe crée, il faut choisir où l'instancier :

use Symfony\Component\EventDispatcher\EventDispatcher;
...
$this->eventDispatcher->dispatch(new AddExtraDataEvent($userGroup));
 le setter permet de transférer un résultat issu des observateurs de l’évènement, à l'endroit qui les dispatch (qu'il peut récupérer via le getter de l'évènement). Une alternative serait d'injecter à la place de EventDispatcher, un tableau des services des modules concernés, définis par un tag.


ListenerModifier

Pour exécuter une ou plusieurs classes au moment du dispatch, il faut créer maintenant en créer une qui écoute l'évènement. Elle doit peut être reliée à son évènement, soit dans sa déclaration de service pour un écouter (event listener[1]), soit dans son constructeur pour un souscripteur (event subscriber).

Le listener a donc l'inconvénient de devoir être déclaré avec un tag, alors que le subscriber lui, est chargé à chaque exécution du programme, ce qui alourdit légèrement les performances mais évite de maintenir sa déclaration en autowiring.

Exemple de déclaration YAMLModifier

services:
    App\EventListener\MyViewListener:
        tags:
            - { name: kernel.event_listener, event: kernel.view }
class MyViewListener
{
    public function onKernelException(ExceptionEvent $event)
    {
        echo "Triggered!";
    }
}

SubscriberModifier

Un souscripteur doit forcément implémenter EventSubscriberInterface :

class ViewSubscriber implements EventSubscriberInterface
{
    public function getSubscribedEvents(): array
    {
        return [
            KernelEvents::VIEW => ['onView']
        ];
    }

    public function onView(ViewEvent $event): void
    {
        echo "Triggered!";
    }
}

Autre exemple où on veut embarquer dans un évènement maison une information de ses souscripteurs :

class ClientXUserSubscriber implements EventSubscriberInterface
{
    ...
    public static function getSubscribedEvents(): array
    {
        return [
            ClientXEvent::class => 'getProperty',
        ];
    }

    public function getProperty(ClientXUserEvent $event): void
    {
        if ('X' === $this->user->getCompany()) {
            $event->setProperty('XX');
        }
    }
}

DébogageModifier

Les erreurs qui surviennent selon certains évènements ne sont pas faciles à provoquer ou visualiser. Pour les voir sans passer par le profiler, on peut ajouter temporairement dans un contrôleur :

$this->getEventDispatcher()->dispatch('mon_service');

RéférencesModifier