Ответ
Концепция Event Dispatcher (диспетчера событий) является паттерном, и конкретные события зависят от фреймворка или приложения. В контексте Symfony (как эталонной реализации для PHP) можно выделить несколько категорий встроенных событий:
1. События Ядра (HttpKernel): Обрамляют жизненный цикл HTTP-запроса.
KernelEvents::REQUEST(kernel.request): Вызывается в самом начале запроса. Используется для инициализации, проверки прав доступа глобально.KernelEvents::CONTROLLER(kernel.controller): Вызывается непосредственно перед вызовом контроллера. Позволяет изменить контроллер или аргументы (например, для преобразования данных запроса в DTO).KernelEvents::VIEW(kernel.view): Вызывается, если контроллер вернул не объектResponse, а какие-то данные. Позволяет преобразовать эти данные вResponse.KernelEvents::RESPONSE(kernel.response): Вызывается перед отправкой ответа клиенту. Позволяет модифицироватьResponse(добавить заголовки, сжать контент).KernelEvents::FINISH_REQUEST(kernel.finish_request): Вызывается после создания ответа, когда основной запрос обработан.KernelEvents::TERMINATE(kernel.terminate): Вызывается после отправки ответа клиенту. Идеально для «тяжелых» фоновых задач (логирование, отправка email), чтобы не задерживать ответ.KernelEvents::EXCEPTION(kernel.exception): Вызывается при возникновении необработанного исключения. Используется для кастомной обработки ошибок.
2. События Консоли (Console): Аналогичны событиям ядра, но для CLI-команд.
ConsoleEvents::COMMAND,ConsoleEvents::TERMINATE,ConsoleEvents::ERROR.
3. События Компонентов: Другие компоненты Symfony генерируют свои события (например, FormEvents, SecurityEvents).
4. Пользовательские события: Разработчик может создавать свои классы событий для бизнес-логики (например, OrderPlacedEvent, UserRegisteredEvent).
Пример подписки в Symfony через атрибуты:
use SymfonyComponentHttpKernelEventResponseEvent;
use SymfonyComponentEventDispatcherAttributeAsEventListener;
#[AsEventListener(event: KernelEvents::RESPONSE)]
class AddSecurityHeadersListener
{
public function __invoke(ResponseEvent $event): void
{
$response = $event->getResponse();
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
}
} Ответ 18+ 🔞
Ну слушай, раз уж зашла речь про Event Dispatcher, давай разберём, что это за зверь такой. Представь себе, что твой код — это как будто ты сидишь дома, а вокруг тебя происходит овердохуища всего разного. Кто-то дверь открыл, кто-то чайник вскипятил, кто-то заказ в интернете сделал. Так вот, диспетчер событий — это такой центральный ушлый менеджер, который орет на всю квартиру: «Эй, народ! Тут событие чайник.вскипел случилось, все, кто подписан, — по коням!». И тут же выскакивает чувак, который выключает чайник, и второй, который сыпет заварку в чашку. Красота, а не жизнь.
Вот в Symfony, например, этих встроенных событий — как грязи. И они все по полочкам разложены.
1. События Ядра (HttpKernel). Это, блядь, святая святых, они как этапы жизни HTTP-запроса — от рождения до похорон.
KernelEvents::REQUEST(kernel.request): Только запрос приполз, ещё даже не понятно, что он хочет. Идеальный момент, чтобы его обыскать, проверить паспорт (права доступа) или навесить какую-нибудь хуйню в глобальный контекст. Подозрение ебать чувствую к таким ранним событиям.KernelEvents::CONTROLLER(kernel.controller): Контроллер уже найден, вот-вот вызовут. Тут можно последний раз дёрнуть его за рукав: «Слушай, чувак, я тут параметры тебе подправил» или вообще подменить контроллер на своего двойника. Хитрая жопа, короче.KernelEvents::VIEW(kernel.view): А это если твой контроллер — такой оригинал — вернул не готовый ответ, а просто какую-то хуйню: массив, объект,['status' => 'ok']. Событие кричит: «Эй, есть данные, но нет ответа! Кто преобразует?». И тут вылезает, например, сериализатор в JSON и лепит из этого нормальныйResponse.KernelEvents::RESPONSE(kernel.response): Ответ уже готов, но ещё не ушёл к пользователю. Последний шанс его подправить: добавить заголовки безопасности, сжать, куки воткнуть. Работа для перфекционистов.KernelEvents::FINISH_REQUEST(kernel.finish_request): Всё, основная движуха закончилась, ответ сформирован. Можно прибраться за собой в памяти, отвязать какие-нибудь временные штуки. Тихий, почти незаметный этап.KernelEvents::TERMINATE(kernel.terminate): А вот это — мощь! Ответ уже улетел к пользователю, браузер его рисует. А у нас только сейчас начинается тяжёлая работа: логирование, отправка писем, ночные расчёты. Чтобы пользователь не ждал, понимаешь? Гениально, ёпта.KernelEvents::EXCEPTION(kernel.exception): Всё пошло по пизде — вылезло необработанное исключение. Паника! Но это событие — как спасательная служба. Можно перехватить ошибку и показать красивую страницу «404» или «500», а не голый стектрейс. Доверия ебать ноль к коду без такого обработчика.
2. События Консоли (Console). Всё то же самое, но для команд в терминале. COMMAND, TERMINATE, ERROR. Чтоб и там жизнь кипела.
3. События от других компонентов. Формы, безопасность — у них свои тараканы и свои события. FormEvents, SecurityEvents. Там своя кухня, свои разборки.
4. А это самое сокровенное — Пользовательские события. Вот тут твоя фантазия может разгуляться. Сделал заказ — кидай OrderPlacedEvent. Зарегистрировался пользователь — вот тебе UserRegisteredEvent. И пусть все подписчики делают что хотят: менеджеру уведомление, в CRM запись, бонусы начисли. Система становится гибкой, как мартышлюшка.
Ну и как это всё прикрутить, спросишь? Да проще пареной репы сейчас, с атрибутами. Смотри, не отвлекайся:
use SymfonyComponentHttpKernelEventResponseEvent;
use SymfonyComponentEventDispatcherAttributeAsEventListener;
// Вешаем атрибут на класс — и всё, он теперь слушатель.
#[AsEventListener(event: KernelEvents::RESPONSE)]
class AddSecurityHeadersListener
{
// Главное — метод __invoke. Его и вызовут, когда событие случится.
public function __invoke(ResponseEvent $event): void
{
$response = $event->getResponse();
// Навешиваем заголовки безопасности. Чтоб всякие ботоводы не хитрили.
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
// Можно и 'X-Your-Mom-Is-Great', 'YES' добавить, если хочешь.
}
}
Вот и вся магия. Написал класс, объявил его слушателем — и он уже в игре. Фреймворк сам его найдёт, зарегистрирует и будет вызывать. Красота, бля буду. Теперь твой код не куча спагетти, а хорошо организованная вечеринка, где все знают своё место и реагируют на нужные сигналы.