Every framework had an event dispatcher and none of them shared an interface, so a library that wanted to emit events either picked one or invented its own.
interface EventDispatcherInterface
{
public function dispatch(object $event);
}
interface ListenerProviderInterface
{
public function getListenersForEvent(object $event): iterable;
}
// and the one that stops propagation
interface StoppableEventInterface
{
public function isPropagationStopped(): bool;
}
The event is any object and there is no name or string identifier, which is the design decision that makes it typed end to end — listeners are matched by the event’s class rather than by a string somebody typoed. Separating the dispatcher from the listener provider is what lets a framework keep its own registration mechanism while a library only depends on the dispatching half. Adoption in 2019 is early, and its value is entirely for library authors rather than for applications.