sabre/event
Lightweight PHP 8.2+ library for event-driven development: EventEmitter, promises, an event loop, and coroutines. Used to build reactive, non-blocking apps and services. Full docs at sabre.io/event.
Installation:
composer require sabre/event "^6.1"
Ensure your project uses PHP 8.2+ (required for sabre/event v6.1).
Basic Event Emitter:
use Sabre\Event\Emitter;
$emitter = new Emitter();
$emitter->on('event.name', function ($arg) {
echo "Event triggered with: " . $arg;
});
$emitter->emit('event.name', 'test');
First Promise:
use Sabre\Event\Promise;
$promise = new Promise(function ($resolve) {
$resolve('Done!');
});
$promise->then(function ($result) {
echo $result; // Outputs: "Done!"
});
Run the Event Loop (for async operations):
use Sabre\Event\EventLoop\EventLoop;
$loop = new EventLoop();
$loop->run();
sabre/event in Artisan commands, console kernels, or non-HTTP contexts (avoid HTTP routes to prevent blocking).use Sabre\Event\Promise;
use Sabre\Event\EventLoop\EventLoop;
$loop = new EventLoop();
$promise = new Promise(function ($resolve) {
sleep(2); // Simulate I/O
$resolve('Task complete');
});
$promise->then(function ($result) {
echo $result; // Runs after 2 seconds
});
$loop->run(); // Keep the loop alive
$emitter = new Emitter();
$emitter->on('user.created', function ($user) {
// Send welcome email
});
$emitter->on('user.created', function ($user) {
// Log user creation
});
$emitter->emit('user.created', new User());
Event system for hybrid patterns:
$emitter->on('laravel.event', function ($event) {
// Extend Laravel events with custom logic
});
.then()/.otherwise().$promise = new Promise(function ($resolve) {
$data = fetchFromApi();
$resolve($data);
});
$promise->then(function ($data) {
return processData($data);
})->then(function ($result) {
saveToDatabase($result);
})->otherwise(function ($error) {
logError($error);
});
Promise\all() for parallel execution:
$promises = [
fetchUserData(),
fetchOrderData(),
];
Promise\all($promises)->then(function ($results) {
// Both resolved
});
$loop = new EventLoop();
$promise = new Promise(function ($resolve) {
$loop->futureTick(function () use ($resolve) {
$resolve('Async result');
});
});
$promise->then(function ($result) {
echo $result;
});
$loop->run();
yield to pause/resume execution.$loop = new EventLoop();
$coroutine = coroutine(function () {
$result = yield new Promise(function ($resolve) {
sleep(1);
$resolve('Data');
});
echo $result; // Outputs after 1 second
});
$loop->run();
Console Kernel Integration:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('async-task')->everyMinute();
}
Use sabre/event inside the command to run async logic.
Service Providers:
public function boot()
{
$this->app->make(Emitter::class)->on('app.started', function () {
// Initialize async services
});
}
Avoid HTTP Blocking:
Emitter::emit() with multiple listeners for efficiency.Promise objects where possible to avoid overhead.EventLoop tick intervals.Sabre\Event\Emitter in unit tests:
$emitter = new Emitter();
$emitter->on('test', $callback = fn() => 'triggered');
$this->assertEquals('triggered', $emitter->emit('test'));
Promise::wait():
$result = $promise->wait();
$this->assertEquals('expected', $result);
Event Loop Blocks HTTP Requests
$loop->run() in a Laravel HTTP route will block the request indefinitely.PHP 8.2+ Requirement
sabre/event v6.1+ requires PHP 8.2. Older versions (v6.0.x) support PHP 7.4–8.1.composer.json constraints and upgrade PHP if needed.Promises Need the Event Loop
.then() callbacks won’t execute unless the EventLoop is running.$loop->run() for async Promise chains:
$promise->then(...);
$loop->run(); // Required!
Wildcard Listeners Can Overlap
Emitter::on('*.event', ...) may match unintended events.Coroutines Require Generators
coroutine() expects a generator function (uses yield).$coroutine = coroutine(function () {
yield new Promise(...); // Correct
});
Log Event Emissions:
$emitter->on('*', function ($event, $args) {
logger()->debug("Event $event triggered", ['args' => $args]);
});
Inspect Promise States:
$promise->then(...)->otherwise(function ($error) {
logger()->error("Promise rejected: " . $error->getMessage());
});
Event Loop Deadlocks:
$loop->run() hangs indefinitely.Promise callbacks resolve/reject and no infinite loops exist.Priority Conflicts:
$emitter->on('event', $callback, 10); // Higher priority
No Laravel Service Provider
Emitter manually in AppServiceProvider:
$this->app->singleton(Emitter::class, function () {
return new Emitter();
});
Event Loop in Queues
EventLoop in queue workers for async task coordination:
// app/Console/
How can I help you explore Laravel packages today?