Какие события (Events) существуют в системе Event Dispatcher?

Ответ

Концепция 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' добавить, если хочешь.
    }
}

Вот и вся магия. Написал класс, объявил его слушателем — и он уже в игре. Фреймворк сам его найдёт, зарегистрирует и будет вызывать. Красота, бля буду. Теперь твой код не куча спагетти, а хорошо организованная вечеринка, где все знают своё место и реагируют на нужные сигналы.