yiisoft/yii-event
PSR-14 event dispatcher for Yii Framework. Register listeners in DI container config files, bind callables or service methods to events, and dispatch type-safe events through a lightweight, framework-integrated implementation.
yiisoft/app) this package is already installed and wired. Start by reading the merged config: config/common/events.php in your app, plus vendor/yiisoft/yii-event/config/di.php, which binds Psr\EventDispatcher\EventDispatcherInterface to the package dispatcher. Everything you register goes through that binding.composer require yiisoft/yii-event. Note it is a thin integration layer over yiisoft/event-dispatcher (the actual PSR‑14 engine); read both, but treat the latter as the source of truth for dispatcher semantics.// config/common/events.php
return [
'yiisoft/yii-event' => [
'events' => [
App\Event\UserRegistered::class => [
[App\Listener\SendWelcomeEmail::class, 'handle'],
],
],
],
];
final class UserService
{
public function __construct(private EventDispatcherInterface $events) {}
public function register(string $email): void
{
// ...
$this->events->dispatch(new UserRegistered($email));
}
}
EventDispatcherInterface from the container, dispatch a throwaway event from a console command, and run the skeleton's config check (composer check, which exercises the package's listener configuration checker). This catches typos in event/listener class names before runtime.Psr\EventDispatcher\EventDispatcherInterface; services stay framework-agnostic and tests can swap a fake or collecting dispatcher.final class OrderPlaced { public function __construct(public readonly string $orderId) {} } — no base class needed. PSR‑14 dispatches objects, so listeners are keyed by the event class you actually dispatch.[SomeListener::class, 'handle'] — resolved from the container, so constructor DI works.InvokableListener::class — class-string for an invokable service, also container-resolved.config/common/events/foo.php) for modular apps and let yiisoft/config merge them. Ordering matters — put audit/logging handlers first, side-effects last.BeforeRequest/AfterRequest; register cross-cutting concerns (request IDs, timing) there when they're not HTTP-specific enough to justify middleware.StoppableEventInterface where a listener may cancel ("can this be saved?") and check isPropagationStopped() in the dispatcher loop. Use sparingly — it makes flow harder to follow.handle()); use a collecting fake dispatcher in service tests to assert the event was dispatched. Reserve full-container dispatch tests for integration coverage of side effects.yiisoft/queue or similar) for email, webhooks, and long writes rather than doing them inline.new a PSR‑14 dispatcher anywhere, config listeners silently never fire. Always resolve EventDispatcherInterface; if you decorate, decorate the bound interface so registration still flows through the package.SomeListener::class without __invoke() fails; use [Class::class, 'method'] for ordinary methods.dispatch(). Wrap genuinely non-critical listeners in try/catch inside the listener and log; let the error handler manage the rest.priority key — list listeners explicitly or have one coordinating listener.ContainerInterface is tempting; inject concrete dependencies instead.ListenerProviderInterface and rebind, or override the package's listener factory/event config via DI. A decorator around EventDispatcherInterface is the cleanest place for logging, metrics, and tracing while keeping config unchanged.How can I help you explore Laravel packages today?