Рассылка и отчёт¶
dispatch() отправляет событие всем подходящим слушателям и возвращает отчёт.
use Selyusize\EventsRouter\Contract\Core\EventInterface;
use Selyusize\EventsRouter\Contract\Core\ListenerInterface;
use Selyusize\EventsRouter\Contract\Core\MiddlewareInterface;
final class MarkOrderPaid implements ListenerInterface
{
public static function handle(EventInterface $event): void
{
echo ' MarkOrderPaid: заказ ', $event->getAttribute('order_id'), PHP_EOL;
}
}
final class AccrueBonuses implements ListenerInterface
{
public static function handle(EventInterface $event): void
{
throw new RuntimeException('сервис бонусов недоступен');
}
}
final class SendReceipt implements ListenerInterface
{
public static function handle(EventInterface $event): void
{
echo ' SendReceipt: чек на ', $event->getPayload()['amount'], ' ₽', PHP_EOL;
}
}
final class EventLogger implements MiddlewareInterface
{
public function process(EventInterface $event, Closure $next): void
{
echo '→ ', $event->getName(), PHP_EOL;
$next($event);
echo '← ', $event->getName(), PHP_EOL;
}
}
use Selyusize\EventsRouter\Event;
use Selyusize\EventsRouter\EventRouterFactory;
use Selyusize\EventsRouter\Routing\RouteGroup;
$events = EventRouterFactory::create(); // или EventRouterFactory::create($container)
$events->add(EventLogger::class);
$events->group('order', static function (RouteGroup $group): void {
$group->listen('{order_id}.paid', MarkOrderPaid::class);
$group->listen('{order_id}.paid', AccrueBonuses::class);
$group->listen('{order_id}.paid', SendReceipt::class);
});
$report = $events->dispatch(new Event('order.42.paid', ['amount' => 1500]));
foreach ($report->getListeners() as $listener) {
echo $listener->getListener(), ': ', $listener->getStatus()->name;
echo $listener->isFailed() ? ' — ' . $listener->getError()?->getMessage() : '', PHP_EOL;
}
→ order.42.paid
MarkOrderPaid: заказ 42
SendReceipt: чек на 1500 ₽
← order.42.paid
MarkOrderPaid: Handled
AccrueBonuses: Failed — сервис бонусов недоступен
SendReceipt: Handled
Как проходит рассылка¶
dispatch(order.42.paid)
└─ middleware роутера: $events->add(...) один раз на событие
└─ для каждого маршрута в порядке объявления:
middleware групп → middleware маршрута → слушатель
- Роутер находит все подходящие маршруты, как
match(). - Событие проходит через middleware роутера. Они выполняются один раз на событие, даже если слушателей нет.
- Каждый слушатель по очереди, в порядке объявления маршрутов:
- получает копию события с параметрами своего маршрута в атрибутах (
order_id = '42'); - проходит через свои middleware: групп, затем маршрута.
- получает копию события с параметрами своего маршрута в атрибутах (
- Ошибка слушателя записывается в отчёт, дальше действует стратегия.
- Если включён
log_dispatch, рассылка пишется в лог.
Отчёт¶
DispatchReport:
| Метод | Что возвращает |
|---|---|
getEvent() |
событие, переданное в dispatch() |
getListeners() |
ListenerReport по каждому подходящему маршруту, в порядке вызова |
hasListeners() |
были ли у события слушатели |
hasFailures(), getFailures() |
упавшие слушатели |
ListenerReport:
| Метод | Что возвращает |
|---|---|
getStatus() |
ListenerStatusEnum: см. таблицу ниже |
isFailed() |
статус Failed |
getError() |
исключение для Failed, иначе null |
getRoute(), getListener() |
сработавший маршрут и класс его слушателя |
getEvent() |
событие, которое получил слушатель, с параметрами маршрута |
getDuration() |
время работы слушателя вместе с его middleware, в секундах |
Статусы¶
ListenerStatusEnum |
Что случилось |
|---|---|
Handled |
слушатель отработал без исключения |
Failed |
исключение в слушателе или его middleware |
Skipped |
слушатель не вызван: middleware не вызвал $next, рассылку остановили через StoppableEventInterface или стратегией Stop |
Стратегии ошибок¶
Задаются $events->setErrorStrategy(...).
ErrorStrategyEnum |
Обработчик ошибок | Остальные слушатели | Исключение из dispatch() |
|---|---|---|---|
Continue — по умолчанию |
вызывается | вызываются | нет |
Stop |
вызывается | Skipped |
нет |
Throw |
не вызывается | не вызываются | да, исходное |
Исключение из middleware роутера и из самого обработчика ошибок выходит из dispatch() при любой стратегии.
Как этим пользоваться — в рецепте Обработать ошибки слушателей.