Основные события в Symfony

Во время отрботки http-запроса framework Symfony генерирует некоторые события, которые можно обрабатывать, изменяя параметры запроса и ответа сервера.

Рассмотрим некторые из них.

Создание класса EventListener

Чтобы мы смогли перехватывать события нам необходимо создать специальный класс.

Создадим диркторию src/EventListener и в ней класс UserEventListener.

        
    namespace App\EventListener;
    use Symfony\Component\HttpKernel\Event\RequestEvent;

    class UserEventListener
    {
        public function onKernelRequest(RequestEvent $event){
            // ...
        }
        // ...
    }
        
    

Его необходимо прописать в config/services.yaml с указанием всех событий, которые он будет перехватывать.

        
    # config/services.yaml
    App\EventListener\UserEventListener:
        tags:
            - {name: 'kernel.event_listener', event: 'kernel.request'}
            - {name: 'kernel.event_listener', event: 'kernel.controller'}
            - {name: 'kernel.event_listener', event: 'kernel.controller_arguments'}
            - {name: 'kernel.event_listener', event: 'kernel.view'}
            - {name: 'kernel.event_listener', event: 'kernel.response'}
        
    

Событие kernel.request

Вызывается очень рано, перед определением контроллера, который будет обрабатывать запрос.
Бывает полезным, когда нужно добавить в запрос какие-нибудь параметры или данные.
Параметром в функцию onKernelRequest передается событие Symfony\Component\HttpKernel\Event\RequestEvent. Описание представлено ниже.

        
    use Symfony\Component\HttpKernel\Event\RequestEvent;
    class UserEventListener
    {
        public function onKernelRequest(RequestEvent $event){
            $request = $event->getRequest();
            // Можем добавить в запрос параметр
            $request->request->set("event", "kernel request");
        }
        // ...
    }
        
    

Событие kernel.controller

Данное событие вызывается до выполнения контоллера. При желании можно поменять контроллер для обработки. В качестве параметра в функцию onKernelController передается событие Symfony\Component\HttpKernel\Event\ControllerEvent.

        
    use Symfony\Component\HttpKernel\Event\ControllerEvent;
    class UserEventListener
    {
        public function onKernelController(ControllerEvent $event){
            // Сменить контроллер для обработки
            $controller = $event->setController(function()use($event){
                return new Response("change controller");
            });
        }
    }
        
    

Событие kernel.controller_arguments

Вызывается перед передачей аргументов в контроллер, удобно добавлять или изменять аргументы.

Тип передаваемого события: Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent.

        
    use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
    class UserEventListener
    {
        public function onKernelControllerArguments(ControllerArgumentsEvent $event){
            $event->setArguments([$event->getRequest(), ['myParam' => 'my_value']]);
        }
    }
        
    

Ниже представлено получение доступа к новым аргументам в контроллере.

В переменную $params передаются аргументы, добавленные в событии kernel.controller_arguments.

        
    public function indexAction(Request $request, $params = false){
        return $this->render('frontend/pages/index.html.twig', ['params' => $params]);
    }
        
    

Событие kernel.view

Вызывается когда запрос уже обработан контроллером, но ответ не был отправлен.

Удобно использовать для установки стандартного ответа сервера в случае, когда контроллер не вернул ответ.

        
    use Symfony\Component\HttpKernel\Event\ViewEvent;
    use Symfony\Component\HttpFoundation\Response;
    class UserEventListener
    {
        public function onKernelView(ViewEvent $event){
            $response = new Response();
            $content = "Default page";
            $response->setContent($content);
            $event->setResponse($response);
        }
    }
        
    

Событие kernel.response

Событие происходит перед отправкой ответа клиенту.

Позволяет модифицировать Response, добавляя новые/изменяя headers, cookies

В качестве параметра в функцию onKernelResponse передается событие

        
    use Symfony\Component\HttpKernel\Event\ResponseEvent;
    use Symfony\Component\HttpFoundation\Cookie;
    class UserEventListener
    {
        public function onKernelResponse(ResponseEvent $event){
            $response = $event->getResponse();
            $response->headers->setCookie(new Cookie('test_cookie', 'test_value'));
            $event->setResponse($response);
        }
    }
        
    

Событие kernel.terminate

Данное событие происходит после обработки запроса и отправки ответа сервером.

На него удобно вешать "тяжелые" операции, чтобы пользователь долго не ждал ответ от сервера, например отправку почту, формирование бэкапов и т.д.

Класс события: Symfony\Component\HttpKernel\Event\TerminateEvent

        
    use Symfony\Component\HttpKernel\Event\TerminateEvent;
    class UserEventListener
    {
        public function onKernelTerminate(TerminateEvent $event){
            // Любые тяжелые операции
        }
    }
        
    

Событие kernel.exception

Событие происходит при возникновении ошибки во время выполнения https запроса.

        
    use Symfony\Component\HttpKernel\Event\ExceptionEvent;
    use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
    use Symfony\Component\DependencyInjection\Container;
    use Symfony\Component\HttpFoundation\RedirectResponse;
    use Symfony\Component\HttpFoundation\Response;

    class UserEventListener
    {
        private $container;

        public function __construct(Container $container){
            $this->container = $container;
        }

        public function getContainer():?Container{
            return $this->container;
        }

        public function onKernelException(ExceptionEvent $event){
            $exception = $event->getThrowable();
            if($exception instanceof NotFoundHttpException){
                $path = $this->getContainer()->get('router')->generate("frontend_404");
                $response = new RedirectResponse($path);
                $event->setResponse($response);
            }else{
                $message = $this->getContainer()->get('twig')->render('frontend/error.html.twig', [
                    'error'=>$exception->getMessage()
                ]);
                $response = new Response();
                $response->setContent($message);
                $response->setStatusCode(Response::HTTP_NOT_ACCEPTABLE);
                $event->setResponse($response);
            }
        }
    }