Aller au contenu

Programmation PHP avec Symfony/Évènement

Un livre de Wikilivres.

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.


Terminal
Logo
composer require symfony/event-dispatcher


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
 ------- ----------------------------------------------------------------------------- ----------


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.
 Certains évènements de Symfony possèdent une méthode "setResponse" pour renvoyer directement quelque chose à l'utilisateur.

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 YAML

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

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');
        }
    }
}

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');