События и приёмники событий

События и приёмники событий

Во время выполнения приложения Symfony, запускается множество уведомлений событий. Ваше приложение может принимать эти уведомления и отвечать на них, путем выполнения какой-либо части кода.

Ннутренние события, которые предоставляются самой Symfony, определяются в классе KernelEvents. Сторонние пакеты и библиотеки также запускают множество событий и ваше собственной приложение может запускать Специальные события.

Все примеры, показанные в этой статье, используют одно и то же событие KernelEvents::EXCEPTION в целях последовательности. В вашем приложении вы можете использовать любое событие и даже смешивать некоторые из них в одном абоненте.

Создание приёмника событий

Самым распространённым способом принять событие является его регистрация в приёмнике событий:

// src/AppBundle/EventListener/ExceptionListener.php namespace AppBundleEventListener;

use SymfonyComponentHttpKernelEventGetResponseForExceptionEvent; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentHttpKernelExceptionHttpExceptionInterface;

class ExceptionListener {

public function onKernelException(GetResponseForExceptionEvent $event) {

// Вы получите объект исключения из полученного события $exception = $event->getException(); $message = sprintf(

'My Error says: %s with code: %s', $exception->getMessage(), $exception->getCode()

);

// Настройте ваш объект ответа, чтобы он отображал детали исключений $response = new Response(); $response->setContent($message);

// HttpExceptionInterface - это специальный тип исключения, который // содержит статус кода и детали заголовка if ($exception instanceof HttpExceptionInterface) {

$response->setStatusCode($exception->getStatusCode()); $response->headers->replace($exception->getHeaders());
} else {
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);

}

// Отправить изменённый объект ответа событию $event->setResponse($response);

}

}

Tip

Каждое событие получает немного разные типы объекта $event. Для события kernel.exception - это GetResponseForExceptionEvent. Чтобы увидеть, какой тип объекта получает каждый приёмник событий, см. KernelEvents или документацию о конкретном событии, которое вы принимаете.

Теперь, когда класс создан, вам просто нужно зарегистрировать его в качестве сервиса и уведомить Symfony, что он "приёмник" события kernel.exception, путём использования специального "тега":

  • YAML
    1
    2
    3
    4
    5
    6
    # app/config/services.yml
    services:
        app.exception_listener:
            class: AppBundle\EventListener\ExceptionListener
            tags:
                - { name: kernel.event_listener, event: kernel.exception }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    <!-- app/config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="app.exception_listener"
                class="AppBundle\EventListener\ExceptionListener">
    
                <tag name="kernel.event_listener" event="kernel.exception" />
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    // app/config/services.php
    use AppBundle\EventListener\ExceptionListener;
    
    $container
        ->register('app.exception_listener', ExceptionListener::class)
        ->addTag('kernel.event_listener', array('event' => 'kernel.exception'))
    ;
    

Note

Существует необязательный атрибут тега по имени method, который определяет, какой метод использовать при запуске события. По умолчанию, имя метода выглядит как on + "camel-cased event name". Если событие kernel.exception, то метод, выполняемый по умолчанию - onKernelException().

Другим необязательным атрибутом тега является priority, который по умолчанию имеет значение 0 и который контролирует порядок, в котором выполняются приёмники (чем выше приоритет, тем раньше выполняется приёмник). Это удобно, когда вам нужно гарантировать, что один приёмник будет выполнен раньше другого. Приоритеты внутренних приёмников Symfony обычно колеблются от -255 до 255, но ваши собственные приёмники могут использовать любое положительное или отрицательное число.

Создание абонента событий

Еще одним способом принимать события является абонент событий - класс, который определяет один или более методов, которые принимают одно или более событий. Главное отличие от приёмника событий заключется в том, что абоненты всегда знают, какие события они принимают.

В данном абоненте, разные методы могут принимать одно и то же событие. Порядок, в котором выполняются методы, определяется параметром priority каждого метода (чем выше приоритет, тем раньше вызывается метод). Чтобы узнать больше об абонентах событий, см. Диспетчер событий.

Следующий пример иллюстрирует абонента событий, который определяет несколько методов, которые принимают одно и то же событие kernel.exception:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// src/AppBundle/EventSubscriber/ExceptionSubscriber.php
namespace AppBundle\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class ExceptionSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        // вернуть события, их методы и приоритеты
        return array(
           KernelEvents::EXCEPTION => array(
               array('processException', 10),
               array('logException', 0),
               array('notifyException', -10),
           )
        );
    }

    public function processException(GetResponseForExceptionEvent $event)
    {
        // ...
    }

    public function logException(GetResponseForExceptionEvent $event)
    {
        // ...
    }

    public function notifyException(GetResponseForExceptionEvent $event)
    {
        // ...
    }
}

Вот и все! Ваш файл services.yml должен уже быть настроен так, чтобы загружать сервисы из каталога EventSubscriber. Об остальном позаботится Symfony.

Tip

Если ваши методы не вызываются, когда есть исключение, перепроверьте, что вы загружаете сервисы из каталога EventSubscriber и активировали автоконфигурацию. Вы также можете вручную добавить тег kernel.event_subscriber.

События запросов, проверка типов

Одна страница может делать несколько запросов (один главный и множество под-запросов - обычно с помощью внедрения контроллеров </templating/embedding_controllers?). Для главных событий Symfony, вам может понадобиться проверить, относится ли событие к "главному" запросу или "под-запросу":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// src/AppBundle/EventListener/RequestListener.php
namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class RequestListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            // don't do anything if it's not the master request
            return;
        }

        // ...
    }
}

Некоторые вещи, как то проверка информации в настоящем запросе, могут не понадобиться в приёмниках под-запросов.

Приёмники или абоненты

Приёмники и абоненты могут быть использованы в одном и том же приложении невнятно. Решение использовать что-либо из них, обычно является делом личного вкуса. Однако, существуют некоторые небольшие преимущества у каждого из них:

  • Абонентов проще использовать повторно так как знание событий хранится в классе, а не в определении сервиса. Это то, почему Symfony использует абонентов изнутри;
  • Приёмники более гибкие так как пакеты могут активировать или деактивировать каждый из них, в зависимости от значений конфигурации.

Отладка приёмников событий

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

1
$ php bin/console debug:event-dispatcher

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

1
$ php bin/console debug:event-dispatcher kernel.exception

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.