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"
Requires PHP 8.2+ (check composer.json for project compatibility).
First Use Case: Basic Event Emitter
use Sabre\Event\Emitter;
$emitter = new Emitter();
$emitter->on('greet', function ($name) {
echo "Hello, $name!\n";
});
$emitter->emit('greet', 'Laravel');
Output: Hello, Laravel!
Where to Look First:
src/Emitter.php (core event handling).src/Promise.php (async workflows).src/EventLoop.php (non-blocking execution).$emitter = new Emitter();
$emitter->on('user.created', fn($user) => UserMailer::sendWelcome($user));
$emitter->emit('user.created', new User());
Event facade for hybrid systems:
$emitter->on('order.processed', fn($order) => event(new OrderProcessed($order)));
then()/otherwise().
use Sabre\Event\Promise;
$promise = new Promise(function ($resolve, $reject) {
if (rand(0, 1)) {
$resolve('Success!');
} else {
$reject(new Exception('Failed!'));
}
});
$promise->then(function ($result) {
echo "Resolved: $result\n";
})->otherwise(function ($error) {
echo "Rejected: {$error->getMessage()}\n";
});
Promise\all() to parallelize multiple async tasks:
$promises = [
new Promise(fn($resolve) => $resolve('Task 1')),
new Promise(fn($resolve) => $resolve('Task 2')),
];
Promise\all($promises)->then(fn($results) => dd($results)); // ['Task 1', 'Task 2']
use Sabre\Event\EventLoop;
$loop = new EventLoop();
$emitter = new Emitter();
$emitter->on('tick', function () {
echo "Looping...\n";
$this->emit('tick'); // Re-trigger after delay
});
$loop->futureTick(1000, $emitter, 'tick'); // Emit 'tick' every 1s
$loop->run(); // Blocks until loop stops
queue:work for custom async CLI logic (e.g., real-time data processing).coroutine() to yield control (e.g., pause/resume async tasks).
function* asyncTask() {
yield; // Pause execution
echo "Resumed!\n";
return "Done";
}
$coroutine = coroutine(asyncTask());
$loop = new EventLoop();
$loop->futureTick(1000, $coroutine, 'resume'); // Resume after 1s
$loop->run();
$promise = new Promise(fn($resolve) => $resolve('Data'));
$promise->then(fn($data) => yield); // Yield while waiting
* (e.g., logging, analytics).
$emitter = new Emitter();
$emitter->on('*', function ($event, $data) {
logger()->info("Event $event triggered with: " . json_encode($data));
});
$emitter->emit('user.login', ['id' => 1]);
Event Loop Blocks HTTP Requests
$loop->run() in a Laravel HTTP route freezes the request.// ❌ Avoid in routes
// $loop->run();
// ✅ Safe in commands
$loop->run();
Promises Require Event Loop for then()
then()/otherwise() callbacks won’t execute without an active loop.Promise::wait() for synchronous waits or run in a loop:
$promise->wait(); // Blocks until resolved/rejected
// OR
$loop->futureTick(0, $promise); // Schedule promise in loop
Coroutines Need Explicit Resume
yield pauses indefinitely without a resume trigger.yield with futureTick or manual resume:
$coroutine = coroutine(function* () {
yield; // Must resume later!
});
$loop->futureTick(1000, $coroutine, 'resume');
PHP 8.2+ Strict Typing
v6.0.x for PHP 7.4–8.1 or upgrade to PHP 8.2+.Memory Leaks with Listeners
removeListener() can accumulate callbacks.once() for one-time events:
$emitter->once('event', $callback); // Auto-removes after first emit
$emitter->on('*', fn($event, $data) => logger()->debug("Event $event: $data"));
Promise::wait() to debug async flows:
try {
$result = $promise->wait();
} catch (\Throwable $e) {
logger()->error($e);
}
EventLoop::stop():
$loop->futureTick(5000, fn() => $loop->stop()); // Stop after 5s
Emitter for domain-specific logic:
class UserEmitter extends Emitter {
public function emitLogin(User $user) {
$this->emit('user.login', $user);
}
}
function loggingPromise(Promise $promise) {
return new class($promise) extends Promise {
public function __construct(private Promise $promise) {}
public function then(callable $onFulfilled) {
return $this->promise->then(function ($result) use ($onFulfilled) {
logger()->info("Promise resolved: $result");
return $onFulfilled($result);
});
}
};
}
$loop->futureTick(1000, fn() => echo "Timer fired!\n");
php artisan my:command).php artisan queue:work).scheduler:run).Event facade:
$emitter->on('user.created', fn($user) => event(new UserCreated($user)));
$app->singleton(Emitter::class, fn() => new Emitter
How can I help you explore Laravel packages today?