bodaclick/async-event-dispatcher
Async event dispatcher for PHP inspired by Symfony. Add one or more drivers (listeners) and dispatch AsyncEventInterface events using a fire-and-forget pub/sub style. Includes RabbitMQ and file drivers, with an easy interface for custom drivers.
Installation:
composer require bodaclick/async-event-dispatcher:1.0.x-dev
Ensure your composer.json includes the package under require.
Basic Initialization:
use BDK\AsyncEventDispatcher\AsyncEventDispatcher;
use BDK\AsyncEventDispatcher\AsyncDriver\RabbitMQDriver;
$dispatcher = new AsyncEventDispatcher();
$driver = new RabbitMQDriver(['connection_params' => [...]]);
$dispatcher->addDriver($driver); // Registers for ALL events
First Use Case:
Create an event class implementing AsyncEventInterface:
class UserRegisteredEvent implements AsyncEventInterface {
public $userId;
public function __construct($userId) { $this->userId = $userId; }
}
Dispatch it:
$event = new UserRegisteredEvent(123);
$dispatcher->dispatch($event);
AsyncEventDispatcher class: Core logic for adding drivers and dispatching events.AsyncDriver implementations: Check RabbitMQDriver or FileDriver for configuration patterns.AsyncEventInterface: Minimal contract for events (no methods required, just a marker).Driver Registration:
addDriver($driver, 'user.registered')).addDriver($driver)). Useful for cross-cutting concerns like logging.boot() method).Event Dispatching:
foreach ($userEvents as $event) {
$dispatcher->dispatch($event);
}
Driver Configuration:
$fileDriver = new FileDriver(['storage_path' => storage_path('app/async_events')]);
Integration with Laravel:
$this->app->singleton(AsyncEventDispatcher::class, function ($app) {
$dispatcher = new AsyncEventDispatcher();
$dispatcher->addDriver(new RabbitMQDriver($app['config']['rabbitmq']));
return $dispatcher;
});
public function handle(UserRegistered $event) {
$asyncDispatcher->dispatch(new UserRegisteredEvent($event->user->id));
}
Event Serialization:
json_encode()/json_decode() or a library like spatie/array-to-object.Driver-Specific Errors:
try {
$dispatcher->dispatch($event);
} catch (Exception $e) {
Log::error("Async dispatch failed: " . $e->getMessage());
}
Event Serialization:
#[Spatie\LaravelData\Attributes\With] or similar for complex objects.Ordering Guarantees:
Driver Overhead:
Event::DISPATCHING).Memory Leaks:
terminate() or use dependency injection.Log Events: Add a debug driver to log dispatched events:
$dispatcher->addDriver(new class implements AsyncEventDriverInterface {
public function handle(AsyncEventInterface $event) {
Log::debug("Async event dispatched: " . get_class($event));
}
});
Check Drivers: Verify drivers are registered:
$drivers = $dispatcher->getDrivers(); // Hypothetical method; may need reflection.
Test Locally:
Use the FileDriver for testing (no external dependencies):
$dispatcher->addDriver(new FileDriver(['storage_path' => __DIR__ . '/tests/events']));
Custom Drivers:
Implement AsyncEventDriverInterface:
class SlackDriver implements AsyncEventDriverInterface {
public function handle(AsyncEventInterface $event) {
$webhook = config('services.slack.webhook');
Http::post($webhook, ['text' => "Event: " . get_class($event)]);
}
}
Event Prioritization:
Extend the dispatcher to support priority queues (e.g., by adding a priority property to events and sorting drivers).
Retry Logic: Wrap drivers in a retry decorator:
class RetryDriver implements AsyncEventDriverInterface {
public function __construct(private AsyncEventDriverInterface $driver, private int $retries) {}
public function handle(AsyncEventInterface $event) {
retry($this->retries, function () use ($event) {
$this->driver->handle($event);
});
}
}
Event Validation: Add a validation driver to reject malformed events:
$dispatcher->addDriver(new class implements AsyncEventDriverInterface {
public function handle(AsyncEventInterface $event) {
if (!property_exists($event, 'requiredField')) {
throw new InvalidArgumentException("Event missing required field.");
}
}
});
RabbitMQ Driver:
php-amqplib (composer require php-amqplib/php-amqplib).async_events). Override via constructor:
new RabbitMQDriver(['exchange' => 'custom_exchange']);
File Driver:
class CustomFileDriver extends FileDriver {
protected function serialize(AsyncEventInterface $event): string {
return json_encode(['type' => get_class($event), 'data' => $event->toArray()]);
}
}
How can I help you explore Laravel packages today?