PHP Event Dispatcher PSR-14
Create an Event dispatcher is very easy
Install the standard interfaces for event handling.:
composer require psr/event-dispatcher
Create EventDispatcher class to disptach event
<?php
namespace DevCoder\Listener;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\ListenerProviderInterface;
use Psr\EventDispatcher\StoppableEventInterface;
/**
* Class EventDispatcher
*/
class EventDispatcher implements EventDispatcherInterface
{
/**
* @var ListenerProviderInterface
*/
private $listenerProvider;
/**
* EventDispatcher constructor.
* @param ListenerProviderInterface $listenerProvider
*/
public function __construct(ListenerProviderInterface $listenerProvider)
{
$this->listenerProvider = $listenerProvider;
}
/**
* @param object $event
* @return object
*/
public function dispatch(object $event): object
{
if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
return $event;
}
foreach ($this->listenerProvider->getListenersForEvent($event) as $listener) {
$listener($event);
}
return $event;
}
}
Create ListenerProvider class to sorage all listener
<?php
namespace DevCoder\Listener;
use Psr\EventDispatcher\ListenerProviderInterface;
/**
* Class ListenerProvider
* @package DevCoder\Listener
*/
class ListenerProvider implements ListenerProviderInterface
{
/**
* @var array
*/
private $listeners = [];
/**
* @param object $event
* An event for which to return the relevant listeners.
* @return iterable[callable]
* An iterable (array, iterator, or generator) of callables. Each
* callable MUST be type-compatible with $event.
*/
public function getListenersForEvent(object $event): iterable
{
$eventType = get_class($event);
if (array_key_exists($eventType, $this->listeners)) {
return $this->listeners[$eventType];
}
return [];
}
/**
* @param string $eventType
* @param callable $callable
* @return $this
*/
public function addListener(string $eventType, callable $callable): self
{
$this->listeners[$eventType][] = $callable;
return $this;
}
/**
* @param string $eventType
*/
public function clearListeners(string $eventType): void
{
if (array_key_exists($eventType, $this->listeners)) {
unset($this->listeners[$eventType]);
}
}
}
Create Event class
<?php
namespace DevCoder\Listener;
use Psr\EventDispatcher\StoppableEventInterface;
/**
* Class Event
* @package DevCoder\Listener
*/
class Event implements StoppableEventInterface
{
/**
* @var bool Whether no further event listeners should be triggered
*/
private $propagationStopped = false;
/**
* Is propagation stopped?
*
* This will typically only be used by the Dispatcher to determine if the
* previous listener halted propagation.
*
* @return bool
* True if the Event is complete and no further listeners should be called.
* False to continue calling listeners.
*/
public function isPropagationStopped(): bool
{
return $this->propagationStopped;
}
/**
* Stops the propagation of the event to further event listeners.
*
* If multiple event listeners are connected to the same event, no
* further event listener will be triggered once any trigger calls
* stopPropagation().
*/
public function stopPropagation(): void
{
$this->propagationStopped = true;
}
}
Create an Event
<?php
namespace App\Event;
use App\Entity\User;
use DevCoder\Listener\Event;
/**
* Class PreCreateEvent
* @package App\Event
*/
class PreCreateEvent extends Event
{
/**
* @var object
*/
private $object;
/**
* PreCreateEvent constructor.
* @param object $object
*/
public function __construct(object $object)
{
$this->object = $object;
}
/**
* @return object
*/
public function getObject(): object
{
return $this->object;
}
}
Create listener
<?php
namespace App\Listener;
use App\Entity\User;
use App\Event\PreCreateEvent;
/**
* Class SecurityListener
* @package App\Listener
*/
class UserListener
{
/**
* @param PreCreateEvent $event
*/
public function __invoke(PreCreateEvent $event): void
{
$object = $event->getObject();
if ($object instanceof User) {
// do something
}
}
}
How to use ?
$listenerProvider = (new ListenerProvider())
->addListener(PreCreateEvent::class, new UserListener());
$dispatcher = new EventDispatcher($listenerProvider);
// After flush user in database send event
$dispatcher = new EventDispatcher($listenerProvider);
$dispatcher->dispatch(new PreCreateEvent($user));
UserListener::class will be automatically call
Ideal for small project
Simple and easy!
https://github.com/devcoder-xyz/php-event-dispatcher
Top comments (5)
For which use-cases could this event dispatcher be used?
Do you have some real-world examples?
Consider the real-world example where you want dispatch event when a user login
The event listener will be executed to set last connexion date :
$user->setLastLogin(new \DateTime());
$user->save();
Thanks for your answer. Why should I store the user's last login date with an event dispatcher when I could do the same without events?
What if there are two ways to authenticate on your application, Login and password | JWT ? you would have to duplicate the code. With a single event you fixes the problem.
In addition, give you the possibility of more flexibility without modifying the core of your code.
If I have two ways to authenticate I would separate the use-case specific logic into different (application) services and the shared logic into a domain service class.
Some comments have been hidden by the post's author - find out more