首先我们先重点关注 kernel.request
事件. 通常在进行 Controller 执行具体的业务代码之前, 系统都需要做一些初始化, 身份检查之类的工作.
这些公共的行为如果定义到每个 Controller 里去是非常不方便维护的. 通常需要进行一个 Plugin 或者中间件来处理. 而这些操作通常通过一个钩子在系统最初阶段进行执行.
在 Symfony 这个框架里, Listener 可以很好的处理这部分操作.
先看一下系统默认的 kernel.request
监听事件处理机制.
$ bin/console debug:event-dispatcher kernel.request
可以看到系统已经注册了8个监听器:
Registered Listeners for "kernel.request" Event
===============================================
------- ------------------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- ------------------------------------------------------------------------------------------------- ----------
#1 Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure() 2048
#2 Symfony\Component\HttpKernel\EventListener\ValidateRequestListener::onKernelRequest() 256
#3 Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelRequest() 128
#4 Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest() 32
#5 Symfony\Bundle\FrameworkBundle\EventListener\ResolveControllerNameSubscriber::onKernelRequest() 24
#6 Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelRequest() 16
#7 Symfony\Component\HttpKernel\EventListener\TranslatorListener::onKernelRequest() 10
#8 Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::onKernelRequest() 8
------- ------------------------------------------------------------------------------------------------- ----------
需要注意一下每个监听器有自己的 Priority
优先级. 优先级越高越先执行.
我们来定义一个监听器来监听 kernel.request
事件. 假设用户访问每一个网页的时候我们需要先确定提供给用户什么语言界面.
我们开始编写PHP代码: src/EventListener/LangListener.php
<?php
/**
* LangListener.php
*
*/
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LangListener
{
public function __invoke(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
dump( __METHOD__ . ' Is not master request, ignore it.');
return;
}
$request = $event->getRequest();
//系统允许的 Locale 设置.
$allowedLocales = explode(',', getenv('APP_LOCALE'));
dump($allowedLocales);
//检查当前的 Cookie 设定
$cookieLocale = $request->cookies->get('locale');
dump(sprintf('Cookie 选中的 Locale: %s', $cookieLocale));
if (empty($cookieLocale) || !in_array($cookieLocale, $allowedLocales)) {
$acceptLang = @$request->server->getHeaders()['ACCEPT_LANGUAGE'];
dump(sprintf('当前浏览器语言: %s', $acceptLang));
$locale = substr($acceptLang, 0, 2);
if (!in_array($locale, $allowedLocales)) {
$locale = getenv('APP_LOCALE_DEFAULT');
}
$request->cookies->set('locale', $locale);
dump(sprintf('当前设定 Locale: %s', $locale));
}
}
}
监听器编写完成后. 需要配置到系统, 让系统注册号该监听器. 修改 config/service.yaml
在 services:
添加下面注册信息.
# ...
services:
# ...
App\EventListener\LangListener:
tags: [{name: kernel.event_listener, event: kernel.request, priority: 99}]
如此我们就给系统提供了一个全局的监听器, 在这每个 Controller
里只需要从 cookie
里提取网站语言信息即可.
除了通过 services.yaml
里注册监听器, 另外一种方法在 Listener 里自己实现注册监听器.
我们再编写一个 AuthListener
来检查用户是否有权限访问某个网页: src/EventListener/AuthListener.php
<?php
/**
* AuthListener.php
*
*/
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class AuthListener implements EventSubscriberInterface
{
/**
* 注册监听器
*
* @return array
*/
public static function getSubscribedEvents()
{
//可声明注册多个类型的事件监听器, 同一个事件也可以绑定多个监听方法.
return [
KernelEvents::REQUEST => [
['onKernelRequest', 30], //监听方法1
//...
],
//KernelEvents::CONTROLLER => [['onKernelController', 99]],
//...
];
}
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
dump( __METHOD__ . ' Is not master request, ignore it.');
return;
}
dump('Check user identity here');
}
public function onKernelController(GetResponseEvent $event)
{
// todo
}
}
如此一来, 我们使用命令看看监听器是否已经添加到系统里:
$ bin/console debug:event-dispatcher kernel.request
可以看到已经注册成功:
Registered Listeners for "kernel.request" Event
===============================================
------- ------------------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- ------------------------------------------------------------------------------------------------- ----------
#1 Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure() 2048
#2 Symfony\Component\HttpKernel\EventListener\ValidateRequestListener::onKernelRequest() 256
#3 Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelRequest() 128
#4 App\EventListener\LangListener::__invoke() 99
#5 Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest() 32
#6 App\EventListener\AuthListener::onKernelRequest() 30
#7 Symfony\Bundle\FrameworkBundle\EventListener\ResolveControllerNameSubscriber::onKernelRequest() 24
#8 Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelRequest() 16
#9 Symfony\Component\HttpKernel\EventListener\TranslatorListener::onKernelRequest() 10
#10 Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener::onKernelRequest() 8
------- ------------------------------------------------------------------------------------------------- ----------
需要注意的几点:
-
Listener
的执行顺序是按Priority
从高到低来执行的. 这个非常重要. - 如果
Listener
的监听方法返回了一个Response
, 那么该事件的派遣Dispatch
就会结束, 接下来会进入到kernel.response
事件处理流程中. 所以Priority
的值就需要注意设置好. - 在
services.ymal
中注册 Listener 的时候, tags 可以method
的属性可以指定监听该事件的方法.- 如果配置了
method
, 那么调用该Listener
就执行指定的method
, 如果没有配置method
. - 如果没配置
method
属性, 那么会优先查找Listener
的onKernelRequest()
方法. - 如果既没有指定
method
方法, 也未定义onKernelRequest()
方法, 那么会尝试执行__invoke()
魔术方法. - 如果以上三种都未定义, 那么会抛出异常.
- 如果配置了
- 在
kernel.request
事件机制中, 所有的 Listener 中的响应方法接收到的参数是:GetResponseEvent
, 但是在其他的事件机制中, 传入的参数是不同的. 具体的可以参考官方文档: https://symfony.com/doc/current/components/http_kernel.html#component-http-kernel-event-table
到此可以基本初步了解窥视一下 Symfony 的事件机制. 也可以自己定义事件加入到系统中, 再自主定义 Listener 来响应事件.
网友评论