[022] Symfony4 事件机制 Port02

2019-03-12  本文已影响0人  观星汉

首先我们先重点关注 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.yamlservices: 添加下面注册信息.

# ...
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         
 ------- ------------------------------------------------------------------------------------------------- ---------- 

需要注意的几点:

到此可以基本初步了解窥视一下 Symfony 的事件机制. 也可以自己定义事件加入到系统中, 再自主定义 Listener 来响应事件.

上一篇 下一篇

猜你喜欢

热点阅读