Event dispatching, listeners, and broadcasting for real-time features
Lyger provides a powerful event system for decoupling application logic and enabling real-time features. Events can be dispatched, listened to, and broadcast across channels.
Events extend the base Event class and contain data about what happened:
Copy
use Lyger\Events\Event;class UserRegistered extends Event{ public function __construct( public readonly int $userId, public readonly string $email, public readonly string $name ) {}}class OrderPlaced extends Event{ public function __construct( public readonly int $orderId, public readonly float $total, public readonly array $items ) {}}
Events automatically get their name from the class name. UserRegistered becomes “UserRegistered” in the event system.
// Listen to all user eventsEventDispatcher::listen('User*', function($event, $payload) { logger()->info("User event: " . $event->getName());});// Listen to all order eventsEventDispatcher::listen('Order*', function($event) { updateDashboard($event);});// Listen to all eventsEventDispatcher::listen('*', function($event) { auditLog($event);});
Wildcard listeners run for every matching event. Use them sparingly to avoid performance issues.
use Lyger\Events\Event;use Lyger\Events\ShouldBroadcast;use Lyger\Events\Channel;use Lyger\Events\PrivateChannel;class MessageSent extends Event implements ShouldBroadcast{ public function __construct( public readonly int $chatId, public readonly int $userId, public readonly string $message ) {} public function broadcastOn(): array { return [ new PrivateChannel("chat.{$this->chatId}") ]; } public function broadcastAs(): string { return 'message.sent'; } public function broadcastWith(): array { return [ 'chat_id' => $this->chatId, 'user_id' => $this->userId, 'message' => $this->message, 'timestamp' => time() ]; }}
use Lyger\Events\PrivateChannel;// Requires authentication$channel = new PrivateChannel("user.{$userId}");$channel = new PrivateChannel("chat.{$chatId}");
class NotificationSent extends Event implements ShouldBroadcast { public function __construct( public int $userId, public string $title, public string $message ) {} public function broadcastOn(): array { return [new PrivateChannel("user.{$this->userId}")]; } public function broadcastAs(): string { return 'notification'; } public function broadcastWith(): array { return [ 'title' => $this->title, 'message' => $this->message, 'timestamp' => time() ]; }}// Send notification$event = new NotificationSent(123, 'New Message', 'You have a new message');EventDispatcher::dispatch($event);// Automatically broadcasts to WebSocket channel: private-user.123
// Check if event has listenersif (EventDispatcher::hasListeners('UserRegistered')) { echo "Event has listeners";}// Get listener count$count = EventDispatcher::getListenerCount('UserRegistered');echo "Number of listeners: {$count}";
use Lyger\Events\FakeBroadcast;// Record events instead of broadcastingFakeBroadcast::record('MessageSent');// Assertionsassert(FakeBroadcast::assertDispatched('MessageSent'));assert(FakeBroadcast::assertNotDispatched('OrderPlaced'));// Get recorded events$events = FakeBroadcast::events();// ResetFakeBroadcast::reset();
For expensive operations, dispatch jobs instead of blocking:
Copy
EventDispatcher::listen('UserRegistered', function($event) { // Don't do this (blocks the request) sendWelcomeEmail($event->email); // Do this instead (async) SendWelcomeEmailJob::dispatch($event->email);});
Event Batching
Batch similar events to reduce overhead:
Copy
class EventBatcher { private static array $batch = []; public static function add(Event $event): void { self::$batch[] = $event; } public static function flush(): void { foreach (self::$batch as $event) { EventDispatcher::dispatch($event); } self::$batch = []; }}// Collect eventsEventBatcher::add(new UserViewed(123));EventBatcher::add(new UserViewed(124));// Process in batchEventBatcher::flush();