Changelog¶
Формат — Keep a Changelog, версии — SemVer. До 1.0 API может меняться в минорных версиях.
Unreleased¶
0.7.5 — 2026-10-11¶
Добавлено¶
BitrixEventBridge::dispatch()— свои события проекта в проекте на Bitrix. Событие уходит в тот же роутер, к которому подключён мост: маршруты, middleware, лог и обработка ошибок общие с событиями Bitrix. Работает из компонента, контроллера, агента и из слушателя события Bitrix — например, чтобы перевестиsale.OnSaleOrderPaidв свойshop.order.42.paid. СattachLazy()роутер собирается при первом событии, своём или Bitrix, один раз на процесс. До подключения моста или с топиком под префиксомbitrix—LogicException.- Рецепт «Подключить события Bitrix»: раздел «Свои события рядом с событиями Bitrix» с примером, который выполняется в CI.
Изменено¶
attachLazy()без файла событий больше не собирает роутер второй раз при первом событии Bitrix: берёт уже собранный.
Удалено¶
- Документация на английском (
/events-router/en/). Сообщения библиотеки на английском ('locale' => 'en') остались, ссылки в них теперь ведут на страницы ошибок русского сайта.
0.7.0 — 2026-10-11¶
Добавлено¶
BitrixEvent::getHandlers()— все обработчики, которые Bitrix вызовет на событие, в порядке вызова: изaddEventHandler()вinit.php, из модулей (b_module_to_module) и сам мост роутера. Список берётся изEventManager, поэтому в нём обработчики любых неймспейсов, а не только слушатели роутера.
0.6.0 — 2026-10-10¶
Добавлено¶
- Сообщения на английском. Ключ конфига
locale:EventRouterFactory::create($container, ['locale' => 'en'])переключает на английский тексты исключений, записи лога, предупреждения PHP и комментарий в файле кэша маршрутов. Ссылки в исключениях ведут на английскую версию сайта. По умолчанию —ru, как раньше. Язык общий на процесс (Locale\Messages);create()без ключа его не меняет. Неверное значение —InvalidConfig. - Документация на английском: https://selyusize.github.io/events-router/en/, переключатель языка в шапке сайта. Английские примеры в
examples/en/выполняются в CI, как и русские. Справочник API и список изменений — только на русском. - Политика безопасности
SECURITY.md: как сообщить об уязвимости. - Dependabot: еженедельные обновления зависимостей и действий CI.
Изменено¶
- CI: у workflow явные минимальные права
permissions, действия обновлены.
0.5.1 — 2026-10-10¶
Исправлено¶
- Документация, «Производительность»: таблица смешивала время целого запроса и одной рассылки. Теперь запрос PHP-FPM и воркер — отдельными таблицами, запрос разложен на подготовку и рассылки по замерам, указано, при скольких разных событиях за запрос Symfony выходит вперёд (больше 7).
- Бенчмарк: отдельный сценарий «подготовка без рассылок», время выводится на одну операцию в микросекундах.
- README: строка о производительности называет и выигрыш, и проигрыш.
0.5.0 — 2026-10-10¶
Добавлено¶
- Кэш маршрутов. Ключ конфига
route_cache_fileи методEventRouter::loadRoutes(). Первый вызов собирает таблицу и записывает её в PHP-файл, следующие берут таблицу из файла и не выполняют файл маршрутов и не загружают классы слушателей. Шаблоны хранятся скомпилированными. Middleware роутера из файла маршрутов тоже кэшируются. С кэшем маршруты объявляются только внутриloadRoutes(), middleware — только именем класса, иначеInvalidRoute. Повреждённый или устаревший файл кэша пересобирается. - Рецепт «Включить кэш маршрутов», раздел «Другой контейнер» в рецепте про контейнер.
- Бенчмарк против
symfony/event-dispatcher:make bench, страница «Производительность». Запрос PHP-FPM (200 слушателей + 3 рассылки) с кэшем маршрутов — в 1,8 раза быстрее Symfony.
Изменено¶
- Рассылка в 30 раз быстрее. Поиск маршрутов без перебора: точные имена — в хэш-таблице, шаблоны — по литеральному началу; план рассылки собирается один раз на топик; короткий путь для слушателей без middleware и параметров;
ListenerReportсоздаются лениво, при вызовеgetListeners(); проверенные имена событий запоминаются. - Несовместимо: конструктор
DispatchReportстал внутренним (отчёт создаёт только роутер) и принимает другие аргументы. Методы отчёта не изменились. - Удалён внутренний
RouteGroup::getRevision(). - Примеры и документация подключают файл маршрутов через
$events->loadRoutes(require ...)вместо(require ...)($events). Старый способ работает, пока кэш не включён.
0.4.0 — 2026-10-10¶
Добавлено¶
- Bitrix.
Bitrix\BitrixEventBridgeпереносит событияEventManagerBitrix в маршруты роутера: группаbitrix, топикbitrix.<модуль>.<Событие>.attach()подписывается сразу,attachLazy()— по списку событий из файла и собирает роутер только при первом событии. Один обработчик работает и со старым API (&$arFieldsпо ссылке), и с D7 (Bitrix\Main\Event). Bitrix\BitrixEvent— payload события Bitrix:getFields(),setField(),getArguments(),getD7Event(),cancel(). Отмена возвращает Bitrixfalseс$APPLICATION->ThrowException()илиEventResult::ERRORи останавливает остальных слушателей.BitrixEventBridge::topic()кодирует точку в id партнёрского модуля:rasa.shop→rasa~shop.- Страница «Подключить события Bitrix».
0.3.0 — 2026-10-10¶
Добавлено¶
- Контейнер. Ядро зависит от
php-di/php-di7. Статический фасадContainer\Container(get(),getInstance(),set()) — откуда слушатели берут зависимости.EventRouterFactory::create($container)подключает к нему контейнер проекта, без контейнера работает PHP-DI с автосвязыванием. - Лог в файл.
Service\Log\FileLogger(PSR-3): путь — шаблон с подстановками{date}и{level}, папки создаются сами. Путь задаётся конфигом фабрики:EventRouterFactory::create($container, ['log_path' => '…/{level}/{date}.log']).EventRouter::setLogger()подключает свой логгер вместо файла. - Лог рассылки. Ключ конфига
log_dispatch: на каждое событие одна запись уровняinfo— имя, число слушателей, время, и по каждому слушателю статус, время, атрибуты и ошибка. Событие без слушателей тоже пишется. Payload не пишется. - Конфиг фабрики.
EventRouterFactory::create($container, array $config)с ключамиlog_pathиlog_dispatch. Неизвестный ключ или значение не того типа — исключениеInvalidConfig. - Исключение
InvalidRoute: класс слушателя или middleware в маршруте не найден или не реализует нужный интерфейс.
Изменено¶
- Несовместимо: ошибки слушателей по умолчанию пишутся не в
error_log(), а в лог роутера (PsrLoggerErrorHandler+FileLogger).PhpErrorLogHandlerудалён.psr/logперешёл изsuggestв обязательные зависимости. - Несовместимо: классы проверяются при объявлении маршрута:
listen()иadd()бросаютInvalidRoute. Раньше такой слушатель падал только при рассылке со статусомFailed. - Несовместимо: middleware, указанный именем класса, всегда берётся из контейнера. Ветки
newбез аргументов больше нет: с PSR-11 контейнером без автосвязывания middleware нужно зарегистрировать в нём. Middleware с зависимостями в конструкторе теперь создаются без регистрации (PHP-DI по умолчанию). - Проверки аргументов (
Event,TopicPattern, маршруты) переписаны наwebmozart/assert. Исключения и тексты ошибок прежние, кроме имени параметра в ошибке snake_case:имя параметра "orderId"вместо"{orderId}". - Документация разложена по Diátaxis: «Обучение», «Рецепты», «Справочник», «Как это устроено». Новые страницы: «Подключить контейнер», «Настроить лог», «Конфигурация», «Почему так». Старые адреса
guide/*больше не работают.
0.2.0 — 2026-10-10¶
Изменено¶
- Несовместимо: enum называются с суффиксом
Enum:ErrorStrategy→ErrorStrategyEnum,ListenerStatus→ListenerStatusEnum. - Несовместимо: все интерфейсы перенесены в
Contract\:Exception\ExceptionInterface→Contract\Exception\ExceptionInterface,Routing\RouteCollectorInterface→Contract\Routing\RouteCollectorInterface. - Добавлены
Contract\Routing\RouteGroupInterfaceиRouteInterface: их реализуютRouteGroupиRoute. - Несовместимо:
$nextв middleware — замыкание, как в Laravel:MiddlewareInterface::process(EventInterface $event, Closure $next), вызов дальше —$next($event).EventHandlerInterfaceудалён. - Несовместимо: маршрут, который возвращает
listen(), называетсяRoute(былRouteDefinition); маршрут из таблицы —CompiledRoute(былRoute). Поиск —RouteTable::match(), отдельногоRouteMatcherнет. - Несовместимо:
setErrorHandler()принимает только объект. - Несовместимо: удалено исключение
UnresolvableHandler: опечатка в имени класса слушателя или middleware даёт обычную ошибку PHP и попадает в отчёт. - Шаблон топика компилируется в одно регулярное выражение, сопоставление — один
preg_match. ВнутренниеSegmentиSegmentTypeEnumудалены. - Удалён внутренний
Service\RouteMatcherInterface: у него была одна реализация. - Несовместимо: удалены неиспользуемые методы:
RouteDefinition::name(),Route::getName(),Route::getIndex(),TopicPattern::matches()(используйтеmatch() !== null),TopicPattern::isExact(),TopicPattern::getParameterNames(). Имя маршрута вернётся, когда понадобится для асинхронной обработки.
Исправлено¶
- Документация: примеры кода на страницах показывались как текст, а не как блоки PHP.
0.1.0 — 2026-10-10¶
Первый релиз.
Добавлено¶
- События.
EventInterfaceиEvent: имя-топик, payload, иммутабельные атрибуты. Проверка имени —InvalidEventName. - Шаблоны топиков.
TopicPattern: параметры{order_id}и{order_id:\d+}(имена в snake_case),*— один сегмент,#— ноль и больше сегментов, как в AMQP. Ошибки синтаксиса —InvalidTopicPatternпри регистрации маршрута. - Маршруты в стиле Slim.
listen()— один статичный слушатель на вызов,group()с префиксом (в том числе пустым),add()для middleware (последний добавленный выполняется первым),name(),setPrefix(). Слушатели одного события вызываются строго в порядке объявления, приоритетов нет. - Поиск и рассылка.
match()находит все подходящие маршруты с параметрами.dispatch()прогоняет каждого слушателя через его middleware и возвращаетDispatchReportсо статусамиHandled,Failed,Skippedи временем. Middleware роутера ($events->add()) выполняются один раз на событие. - Обработка ошибок. Ошибка одного слушателя не мешает остальным.
setErrorHandler()(по умолчанию —error_log(), есть PSR-3 обработчик), стратегииErrorStrategy::Continue,Stop,Throw, остановка рассылки через PSR-14StoppableEventInterface. - Контейнер.
EventRouterFactory::create(?ContainerInterface): middleware и обработчик ошибок берутся из PSR-11 контейнера или создаются черезnew. Ошибки —UnresolvableHandler. - Воркеры.
run(EventSourceInterface)рассылает события из источника;InMemoryEventSourceдля тестов. - PSR-14.
Psr14EventDispatcher: роутер подPsr\EventDispatcher\EventDispatcherInterface, объекты безEventInterfaceпревращаются в события через явный маппер. Ошибка —UnmappableEvent. - Документация на GitHub Pages: руководство с исполняемыми примерами, справочник API из PHPDoc, страница на каждое исключение.