sabre/event
sabre/event is a lightweight PHP library for event-based development, offering an event emitter, promises, an event loop, and coroutines. Requires PHP 8.2. Install with composer require sabre/event.
Architecture fit. sabre/event is a low-level primitive library, not an application framework: an emitter, a promise implementation, a cooperative event loop, and generator-based coroutines. For a Laravel codebase, the honest architectural verdict is narrow fit. Laravel already ships a first-class event dispatcher (service-provider registration, queued listeners, model events, broadcasting) and a queue/worker model. sabre/event only pulls its weight in contexts Laravel does not cover well out of the box: custom long-running CLI daemons, socket/WebSocket servers, protocol handlers (this is the same stack lineage as sabre/dav), and plugin-style hook systems that need wildcard listeners and priorities without the framework ceremony. The promise/loop/coroutine trio is also a legitimate alternative to pulling in ReactPHP or amphp for a bounded async workflow — but it is a lower-level choice than fibers-based or Octane-native options available in PHP 8.2+.
Integration feasibility. Technically trivial: composer require sabre/event "^6.1", PSR-4 autoloading, tiny public surface, no framework coupling, no config. The friction is organizational rather than technical — introducing a second event system alongside Illuminate\Events is a genuine cognitive and governance cost. Teams will inevitably ask "which emitter?" and we need a written rule (framework events for domain/business events; sabre/event only inside isolated long-running processes). It should be wrapped behind an internal adapter interface so a future swap to ReactPHP/amphp or a move to Laravel-native primitives is a bounded change.
Technical risk.
^6.1 requires PHP 8.2. If any deployment target, legacy service, or shared package is pinned below 8.2, this becomes a blocking prerequisite, not a library decision.Key questions.
Stack fit. Treat sabre/event as a process-scoped utility, not an application-wide event bus. Appropriate homes: Artisan commands and queue workers that need internal async coordination, custom socket/WebSocket services, plugin registries requiring wildcard and priority listeners, and integration with sabre/dav-family protocol libraries. Inappropriate home: HTTP controllers, domain eventing, anything that competes with Illuminate\Events or Bus. The two systems must not share a mental model — give them distinct names internally (e.g., Internal\Async\* for the sabre layer) and document the split in the engineering handbook.
Migration path. Gate on PHP baseline first, then introduce. Sequence: confirm all runtimes are 8.2+ (Laravel 11 already requires 8.2, so this is often already true); pin ^6.1.0 in the module that needs it, not the root require; use 6.0.x only if a legacy branch on 7.4–8.1 cannot move. Where an existing ad-hoc async pattern exists (manual pcntl, sleep loops, naive polling), migrate one workflow at a time behind an adapter rather than rip-and-replace — the failure modes of the loops differ and simultaneous migration hides regressions.
Compatibility. Composer-level, coexistence with Laravel is clean: no autoload collisions, no service provider, no global state unless you create it. The real compatibility risks are runtime, not packaging: (a) Octane/Swoole worker persistence — the event loop must be created, run, and torn down per invocation, never left resident across requests; (b) PHP version parity between local, CI, and production; (c) behavioral differences between the 6.0 and 6.1 series that could surface if some services float and others pin.
Sequencing.
AsyncRunner interface) wrapping emitter/promise/loop so call sites never touch Sabre\Event\* directly.Maintenance. Small codebase, low churn, permissive BSD-3-Clause — a cheap dependency to carry. The cost is not in upgrades (releases are mostly tooling and PHP-version support) but in the absence of an upgrade narrative: expect no feature investment, and plan to read the source because the published documentation is compact and website-hosted. Budget a small amount of ongoing engineering time for the internal adapter and for re-validating behavior on each PHP minor bump and each Laravel major.
Support. Community issue tracker only; the commercial-support line is a sales surface, not an SLA. For a TPM this means: no vendor to escalate to, no guaranteed response window, and therefore a requirement that we can read, patch, and if necessary fork a ~small library ourselves. Confirm we have at least one engineer who has actually read the emitter and loop implementations before this becomes load-bearing for any revenue path.
Scaling. The event loop is single-threaded and cooperative; throughput scales by running more processes, not by concurrency within a process. Promises are in-process only — this is not a distributed queue and must not be positioned as a Redis/queue substitute for cross-service work. Memory per process grows with listener registrations and pending promises, so listener leak discipline (explicit removeAllListeners, once where appropriate, bounded fan-out) is an operational requirement, not a code-style preference. For high-throughput work, the right pattern is many short-lived processes with tight loops, not one long-lived process accumulating state.
Failure modes. The realistic production incidents, ranked:
sleep, heavy DB call) inside a listener or coroutine halts all in-flight promises. Symptom is latency creep with no error, which is the worst kind.then chain without otherwise silently drops failures; work "succeeds" without completing.wait() — synchronous waiting inside the loop, or nested waits, can hang the process.Mitigations: mandatory otherwise on every chain (enforceable via code review or a static rule), a global loop timeout ceiling, structured logging around loop start/stop, and adapter-level isolation so that a failure in the async subsystem cannot take down request handling.
Ramp-up. Engineers with JavaScript promise/async experience will map the mental model quickly — perhaps half a day to productive, two to three days to safe (the gap is cooperative scheduling discipline and cleanup semantics). Engineers whose only concurrency experience is Laravel's queue model need explicit coaching: the loop does not "run in the background," and the framework's mental model of events-as-listeners does not transfer. The highest-leverage ramp-up investment is a one-page internal guide covering: adapter usage, the never-call-this-in-HTTP rule, otherwise discipline, and listener teardown. Given the compact documentation and the fact that this package will only be justified in a small number of workflows, a two-day onboarding budget per engineer touching it, plus an explicit code-owner, is a reasonable planning assumption.
How can I help you explore Laravel packages today?