PSR-14 w praktyce: Event Dispatcher w PHP
Eventy to taki temat, który w PHP wraca jak bumerang. Każdy framework ma swoje rozwiązania, ale prędzej czy później pojawia się pytanie: „a czemu nie ma jednego standardu?”. No i w końcu jest. PSR-14 wprowadza wspólny interfejs dla systemów event dispatchingu, dzięki czemu aplikacje, biblioteki i frameworki mogą korzystać z tego samego modelu zdarzeń.
Na czym polega PSR-14?
Cała idea jest prosta: mamy event – obiekt, który coś się wydarzyło – i mamy listenerów, którzy na ten event reagują. Dispatcher odpala event, provider podpowiada kogo wołać, a słuchacze robią resztę.
Przykładowa implementacja
W kursie przygotałem minimalistyczną, ale w pełni zgodną z PSR-14 implementację. Oparta jest o trzy główne pliki:
EventDispatcher.php
Dispatcher to centrum całego systemu. Dostaje event i pyta providera: „kto chce na to zareagować?”. Następnie przechodzi przez listę listenerów i odpala ich w odpowiedniej kolejności. Żadnej magii – tylko prosty, przejrzysty kod.
<?php
declare(strict_types=1);
namespace DJWeb\Framework\Events;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\ListenerProviderInterface;
use Psr\EventDispatcher\StoppableEventInterface;
final readonly class EventDispatcher implements EventDispatcherInterface
{
public function __construct(
private ListenerProviderInterface $listenerProvider
) {
}
public function dispatch(object $event): object
{
$listeners = (array) $this->listenerProvider->getListenersForEvent($event);
$listeners = array_filter($listeners, 'is_callable');
foreach ($listeners as $listener) {
if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
break;
}
$listener($event);
}
return $event;
}
}
ListenerProvider.php
To serce rejestracji słuchaczy. Provider wie, które klasy nasłuchują na jakie eventy i zwraca tę listę, gdy poprosi o nią dispatcher. Dzięki temu eventy są całkowicie niezależne od konkretnych listenerów.
<?php
declare(strict_types=1);
namespace DJWeb\Framework\Events;
use Psr\EventDispatcher\ListenerProviderInterface;
class ListenerProvider implements ListenerProviderInterface
{
/**
* @var array<string, array<callable>>
*/
private array $listeners = [];
public function addListener(string $eventClass, callable $listener): void
{
$this->listeners[$eventClass] ??= [];
$this->listeners[$eventClass][] = $listener;
}
public function getListenersForEvent(object $event): iterable
{
$eventClass = $event::class;
return $this->listeners[$eventClass] ?? [];
}
}
EventManager.php
Zamiast żonglować dispatcherem i providerem, mamy prostą fasadę –
EventManager
. To ona udostępnia wygodne API do rejestrowania listenerów
i wywoływania eventów. Dla programisty oznacza to jedno: prostotę i czytelność.
<?php
declare(strict_types=1);
namespace DJWeb\Framework\Events;
use DJWeb\Framework\Config\Config;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\ListenerProviderInterface;
class EventManager
{
public EventDispatcherInterface $dispatcher{
get => $this->_eventDispatcher ??= new EventDispatcher($this->listenerProvider);
}
public ListenerProviderInterface $listenerProvider{
get => $this->_listenerProvider ??= $this->createListenerProvider();
}
private ?EventDispatcherInterface $_eventDispatcher = null;
private ?ListenerProviderInterface $_listenerProvider = null;
public function dispatch(object $event): object
{
return $this->dispatcher->dispatch($event);
}
private function createListenerProvider(): ListenerProviderInterface
{
$provider = new ListenerProvider();
$eventConfig = Config::get('events.listeners');
foreach ($eventConfig as $eventClass => $listeners) {
foreach ((array) $listeners as $listenerClass) {
$provider->addListener($eventClass, $listenerClass);
}
}
return $provider;
}
}
Dlaczego warto?
Dzięki PSR-14 możesz pisać kod oparty o eventy bez przywiązywania się do jednego frameworka. Taka implementacja jest lekka, zgodna z kontraktem i łatwa do rozszerzenia. A jak kiedyś będziesz chciał podmienić dispatcher na inny – żaden problem, bo interfejs pozostaje ten sam.
📖 Oficjalna specyfikacja PSR-14: https://www.php-fig.org/psr/psr-14/
👉 To tylko fragment pełnego kursu MasterPHP!
W kursie znajdziesz kompletną implementację PSR-14 wraz z przykładami:
- jak pisać własne eventy i listenery,
- jak używać EventManagera jako centralnego punktu,
- jak testować eventy w praktyce.