Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Event Dispatcher Contracts Laravel Package

symfony/event-dispatcher-contracts

Interfaces and base abstractions for Symfony’s event dispatching system. Use these contracts to standardize how events and listeners interact, and to build libraries compatible with Symfony components and their proven implementations.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package via Composer:

composer require symfony/event-dispatcher-contracts

First Steps:

  1. Define a PSR-14-compliant event by extending Symfony\Contracts\EventDispatcher\Event:
    use Symfony\Contracts\EventDispatcher\Event;
    
    class OrderPlaced extends Event
    {
        public function __construct(public int $orderId) {}
    }
    
  2. Use Laravel’s native dispatcher (no bridge needed) to dispatch:
    event(new OrderPlaced(123));
    
  3. Listen to events via Laravel’s Listen trait or EventServiceProvider:
    class OrderPlacedListener
    {
        public function handle(OrderPlaced $event): void
        {
            // Logic here
        }
    }
    

First Use Case: Migrate legacy events to PSR-14 for cross-framework reuse (e.g., shared events between a Laravel API and a Symfony CLI tool).


Implementation Patterns

1. Event Design

  • Value Objects: Treat events as immutable value objects (use readonly properties in PHP 8.1+).
  • Named Events: Implement getName() for explicit event identification:
    class UserActivated extends Event
    {
        public const NAME = 'user.activated';
    
        public function getName(): string
        {
            return self::NAME;
        }
    }
    
  • Domain Events: Align event names with domain language (e.g., InvoiceGenerated instead of InvoiceCreated).

2. Dispatcher Integration

  • Laravel’s Native Dispatcher: Works out-of-the-box since Laravel’s Event class extends Symfony\Contracts\EventDispatcher\Event.
  • PSR-14 Dispatchers: Use adapters like league/event or symfony/event-dispatcher for non-Laravel contexts:
    use Symfony\Component\EventDispatcher\EventDispatcher;
    use Symfony\Component\EventDispatcher\GenericEvent;
    
    $dispatcher = new EventDispatcher();
    $dispatcher->dispatch(new GenericEvent($event), 'user.registered');
    
  • Hybrid Approach: Dispatch PSR-14 events via Laravel’s event() helper by wrapping them in a Laravel-compatible event:
    event(new LaravelEventWrapper($psr14Event));
    

3. Listener Registration

  • Dynamic Registration: Use EventDispatcherInterface::addListener() for runtime binding:
    $dispatcher->addListener(OrderPlaced::NAME, fn(OrderPlaced $event) => /* ... */);
    
  • Priority Handling: Leverage PSR-14’s priority system (e.g., EventDispatcherInterface::addListener(..., 100) for low priority).

4. Testing

  • Mock Dispatchers: Use Symfony\Component\EventDispatcher\EventDispatcher in tests:
    $dispatcher = new EventDispatcher();
    $dispatcher->addListener('user.registered', $this->createMock(Listener::class));
    $dispatcher->dispatch(new UserRegistered($user));
    
  • Event Assertions: Verify events were dispatched with assertHasListeners() (custom helper) or assertCount() on mocked listeners.

5. Performance

  • Avoid Overhead: For high-frequency events, consider bypassing the dispatcher for simple cases (e.g., direct method calls).
  • Lazy Loading: Load listeners dynamically (e.g., via service locator) to reduce bootstrap time.

Gotchas and Tips

Pitfalls

  1. No Concrete Implementation:

    • Mistake: Expecting symfony/event-dispatcher-contracts to include a dispatcher.
    • Fix: Pair with symfony/event-dispatcher or Laravel’s Illuminate\Events\Dispatcher.
  2. Missing getName():

    • Mistake: Assuming getName() is required (it’s optional in PSR-14).
    • Fix: Implement it if using named events or custom dispatchers:
      public function getName(): string { return static::NAME; }
      
  3. PHP Version Mismatch:

    • Mistake: Using on PHP < 8.1 (fails due to readonly properties).
    • Fix: Upgrade or use a legacy adapter (e.g., symfony/event-dispatcher:^5.4).
  4. Laravel-Specific Quirks:

    • Mistake: Relying on Laravel’s Event methods (e.g., broadcastOn) in PSR-14 events.
    • Fix: Keep events minimal; use Laravel-specific logic in listeners.
  5. Circular Dependencies:

    • Mistake: Injecting the dispatcher into events (violates PSR-14’s statelessness).
    • Fix: Pass data via event properties or use a service locator.

Debugging Tips

  • Event Inspection: Use dd($event) to verify event properties and getName().
  • Dispatcher Logging: Wrap the dispatcher to log dispatched events:
    $dispatcher = new class implements EventDispatcherInterface {
        private $decorated;
        public function __construct(EventDispatcherInterface $dispatcher) { $this->decorated = $dispatcher; }
        public function dispatch(Event $event, ?string $eventName = null): Event {
            \Log::debug("Dispatching {$eventName ?? get_class($event)}");
            return $this->decorated->dispatch($event, $eventName);
        }
        // Delegate other methods to $this->decorated...
    };
    
  • Listener Debugging: Use strace or Xdebug to trace listener execution order.

Extension Points

  1. Custom Event Attributes: Add metadata via traits or interfaces (e.g., #[Asynchronous]):
    trait AsyncEvent {}
    class PaymentProcessed extends Event implements AsyncEvent {}
    
  2. Dispatcher Decorators: Extend EventDispatcherInterface to add middleware (e.g., logging, validation):
    class LoggingDispatcher implements EventDispatcherInterface {
        private $inner;
        public function __construct(EventDispatcherInterface $dispatcher) { $this->inner = $dispatcher; }
        public function dispatch(Event $event, ?string $eventName = null): Event {
            \Log::info("Dispatching {$eventName ?? get_class($event)}");
            return $this->inner->dispatch($event, $eventName);
        }
    }
    
  3. Event Serialization: Implement JsonSerializable for events used in queues/APIs:
    class OrderShipped extends Event implements JsonSerializable {
        public function jsonSerialize(): array { return ['order_id' => $this->orderId]; }
    }
    

Laravel-Specific Tips

  • Service Provider Integration: Bind the PSR-14 dispatcher as a singleton in AppServiceProvider:
    $this->app->singleton(EventDispatcherInterface::class, function ($app) {
        return new EventDispatcher();
    });
    
  • Queueable Events: Use ShouldQueue on listeners for async processing (works with PSR-14 events):
    class SendWelcomeEmail implements ShouldQueue {
        public function handle(OrderPlaced $event) { /* ... */ }
    }
    
  • Middleware for Events: Use Laravel’s EventServiceProvider to bind PSR-14 listeners:
    protected $listen = [
        OrderPlaced::NAME => [
            SendNotification::class,
            'queue' => SendEmail::class,
        ],
    ];
    

Anti-Patterns

  • Over-Engineering: ❌ Avoid using PSR-14 for simple Laravel-only events (e.g., AuthAttempted). ✅ Reserve for cross-cutting concerns or shared libraries.
  • Tight Coupling: ❌ Injecting EventDispatcherInterface into domain models. ✅ Use dependency injection at the service layer.
  • Ignoring Laravel’s Ecosystem: ❌ Replacing Laravel’s event() helper with PSR-14 dispatchers for no gain. ✅ Leverage Laravel’s built-in system unless interoperability is required.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai