支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
在symfony程序执行期间,大量的事件通知(event notifications)会被触发。你的程序可以监听这些通知,并执行任意代码作为回应。
Symfony自身提供的内部事件,被定义在KernelEvents
类中。第三方Bundle和类库也会触发大量事件,你自己的程序可以触发自定义事件。
本文展示的所有例子,考虑到一致性,使用了相同的KernelEvents::EXCEPTION事件。在你自己的程序中,你可以使用任何事件,甚至在同一订阅器中(subscriber)混合若干事件。
监听一个事件最常用的方式是注册一个事件监听(event listener):
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 37 38 39 40 | // src/AppBundle/EventListener/ExceptionListener.php
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class ExceptionListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
// You get the exception object from the received event
// 你可以从接收到的事件中,取得异常对象
$exception = $event->getException();
$message = sprintf(
'My Error says: %s with code: %s',
$exception->getMessage(),
$exception->getCode()
);
// Customize your response object to display the exception details
// 自定义响应对象,来显示异常的细节
$response = new Response();
$response->setContent($message);
// HttpExceptionInterface is a special type of exception that
// holds status code and header details
// HttpExceptionInterface是一个特殊类型的异常,持有状态码和头信息的细节
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
} else {
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
}
// Send the modified response object to the event
// 发送修改后的响应对象到事件中
$event->setResponse($response);
}
} |
每一个事件,都要接收“类型略有不同”的$event
对象。对于kernel.exception
事件,这个对象是GetResponseForExceptionEvent。要了解每一个“事件监听”所接收到的“事件对象”之类型,参考KernelEvents,或是你要监听的特定事件之文档。
现在,类被创建了,你只需把它注册成服务,然后通过使用一个特殊的“tag”(标签),告诉Symfony这是一个针对kernel.exception
事件的“监听”:
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 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- 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> |
1 2 3 4 5 | // app/config/services.php
$container
->register('app.exception_listener', 'AppBundle\EventListener\ExceptionListener')
->addTag('kernel.event_listener', array('event' => 'kernel.exception'))
; |
有一个可选的tag属性是method
,它定义了“当事件被触发时,哪个方法要被执行”。默认时,方法的名字是on
+“驼峰事件名”。如果事件是kernel.exception
的话,默认执行的方法则是onKernelException()
。
另有一个可选的tag属性是priority
,它的默认值是0
,用来控制监听被执行的顺序(一个监听器的优先级愈高则愈早被执行)。这在你要“确保某个监听在其他监听之前被执行”时是有用的。Symfony的内部监听,其优先级范围是-255
到255
,但你自己的监听可以使用任何正或负的整数。
另一种监听事件的方式是event subscriber事件订阅,它是一个类,定义了一或多个方法,用于监听一或多个事件。同事件监听的主要区别在于,订阅器始终知道它们正在监听的事件是哪一个。
在一个给定的订阅器中,不同的方法可以监听同一个事件。方法被执行时的顺序,通过每一个方法中的priority
参数来定义(优先级愈高则方法愈早被调用)。要了解更多关于订阅器的内容,参考EventDispatcher组件。
下例展示了一个事件订阅,定义了若干方法,监听的是同一个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 37 | // 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 the subscribed events, their methods and priorities
// 返回被订阅的事件,以及它们的方法和属性
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)
{
// ...
}
} |
现在,你只需把这个类注册成服务,并打上kernel.event_subscriber
标签,即可告诉Symofny这是一个事件订阅器:
1 2 3 4 5 6 | # app/config/services.yml
services:
app.exception_subscriber:
class: AppBundle\EventSubscriber\ExceptionSubscriber
tags:
- { name: kernel.event_subscriber } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- 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_subscriber"
class="AppBundle\EventSubscriber\ExceptionSubscriber">
<tag name="kernel.event_subscriber"/>
</service>
</services>
</container> |
1 2 3 4 5 6 7 8 | // app/config/services.php
$container
->register(
'app.exception_subscriber',
'AppBundle\EventSubscriber\ExceptionSubscriber'
)
->addTag('kernel.event_subscriber')
; |
一个单一页面,可以产生若干次请求(一个主请求[master request],然后是多个子请求[sub-requests],典型的像是如何在模板中嵌入控制器)。对于Symfony核心事件,你可能需要检查一下,看这个事件是一个“主”请求还是一个“子”请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 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内部使用订阅器;
监听器更灵活,因为bundles可以有条件地开启或关闭它们,基于配置文件中的某些“选项值”。
使用命令行,你可以找到“哪些监听被注册到事件派遣器”。要显示全部事件及其监听,运行:
1 | $ php bin/console debug:event-dispatcher |
通过指定事件名称,你可以得到针对此特定事件进行注册的监听:
1 | $ php bin/console debug:event-dispatcher kernel.exception |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。