symfony/event-dispatcher
The EventDispatcher component lets application components communicate by dispatching events and listening to them. Register listeners on named events, control priority, and decouple your PHP code with a simple, standalone Symfony library.
Recommendation up front: adopt symfony/event-dispatcher as a transitive/shared-contract dependency and a package-authoring primitive — not as a replacement for illuminate/events. The package is excellent, mature, MIT-licensed, and battle-tested, but in a Laravel codebase its primary value is already captured by Laravel's own dispatcher, and the highest-risk outcome of this evaluation is a two-dispatcher architecture nobody owns.
Architecture fit
Illuminate\Events\Dispatcher with the same conceptual model plus Laravel-specific capability the Symfony component does not have: queued listeners, ShouldQueue/ShouldQueueAfterCommit, wildcard listeners, model events, broadcasting integration, and Event::fake() test helpers. Any story that positions this package as "a drop-in replacement for Laravel's events" is wrong and should be struck from stakeholder material.Integration feasibility
laravel/framework's dependency graph. Adding a direct requirement mainly changes version pinning risk, not capability. Step one of implementation is composer why symfony/event-dispatcher, not composer require.#[AsEventListener] attribute discovery is wired by FrameworkBundle's DI pass, not by the component in isolation. In a Laravel app the attribute is inert unless we write our own registration pass. Budget for that explicitly or drop the attribute from the value case.Technical risk
dispatch() on, which one Octane/queue workers reset, and which one Event::fake() intercepts. Tests silently passing because a listener was registered on the other dispatcher is the classic failure.TraceableEventDispatcher provides "immutable event logs for GDPR/HIPAA compliance." It does not — it is a debug/profiling wrapper that records which listeners ran and their timing inside the Symfony profiler, is not persisted, and is not tamper-evident. Do not put that sentence in front of auditors or Legal. If audit trails are a requirement, that is a separate design (append-only store + domain events), and this package is at most a hook point.^8.1 while other Symfony packages in the tree resolve ^6.4|^7.x produces solver deadlock and forces an unplanned fleet-wide PHP 8.4 upgrade.TraceableEventDispatcher leaks, plus an 8.0.9 fix for reset-during-dispatch, confirm the component behaves differently under Octane/queue workers than under FPM. Any adoption plan must state the worker-reset story.Key questions to answer before committing
illuminate/events + PSR-14 typing does not? Name the module and the coupling it removes.symfony/event-dispatcher already resolved in our lockfile, and at which version? What breaks if we pin it?Stack fit
illuminate/events remains the application bus — it owns HTTP/request lifecycle, model events, queue integration, and Event::fake().symfony/event-dispatcher as an internal library bus for extracted packages and for domain modules we want framework-agnostic. One instance, one clearly documented service id, never autowired by default.EventDispatcherInterface (via symfony/event-dispatcher-contracts) in reusable code rather than either concrete dispatcher. This is the single highest-value, lowest-risk move in the whole assessment and it does not require replacing anything.Migration path
composer why/composer show on the three or four most-coupled services. Confirm resolved version and PHP constraints. Exit: one-page dependency map.Compatibility
^6.4 (LTS) or ^7.4 (LTS) to match the PHP 8.1/8.2+ floors in current services; treat ^8.x as gated on a PHP 8.4 platform decision, and note that 8.0 enforces return types (extenders must be audited).^6.4|^7.4) rather than pinning, so other Symfony packages in the graph can co-resolve.Illuminate\Events\Dispatcher version before relying on contract-only typing; if absent for our Laravel version, the adapter in step 3 carries it.Sequencing Run steps 1–2 in a single sprint in one module. Gate steps 3–4 on a written answer to questions 1 and 5 above. Any request to lead with the v8.1 GA narrative is a sequencing error: it inverts the PHP upgrade and the architectural decision.
Maintenance
composer update churn.Support
Scaling
Failure modes
stopPropagation() in an early listener silently starving later listeners — a debugging cost that surfaces as "logging stopped working" weeks later.TraceableEventDispatcher in a design review as the audit log. Kill this early; it is the failure mode with external consequences.Ramp-up
#[AsEventListener] attributes are the one genuinely unfamiliar element and, since discovery requires a FrameworkBundle-style DI pass we would have to build, I would deprioritize them for the first two quarters and use explicit registration.How can I help you explore Laravel packages today?