美文网首页
[022] Symfony4 事件机制 Port02

[022] Symfony4 事件机制 Port02

作者: 观星汉 | 来源:发表于2019-03-12 20:47 被阅读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         
 ------- ------------------------------------------------------------------------------------------------- ---------- 

需要注意的几点:

  • Listener 的执行顺序是按 Priority 从高到低来执行的. 这个非常重要.
  • 如果 Listener 的监听方法返回了一个 Response, 那么该事件的派遣Dispatch就会结束, 接下来会进入到kernel.response事件处理流程中. 所以 Priority 的值就需要注意设置好.
  • services.ymal 中注册 Listener 的时候, tags 可以 method 的属性可以指定监听该事件的方法.
    • 如果配置了method, 那么调用该 Listener 就执行指定的 method, 如果没有配置 method.
    • 如果没配置method属性, 那么会优先查找 ListeneronKernelRequest() 方法.
    • 如果既没有指定method方法, 也未定义 onKernelRequest() 方法, 那么会尝试执行 __invoke() 魔术方法.
    • 如果以上三种都未定义, 那么会抛出异常.
  • kernel.request 事件机制中, 所有的 Listener 中的响应方法接收到的参数是: GetResponseEvent, 但是在其他的事件机制中, 传入的参数是不同的. 具体的可以参考官方文档: https://symfony.com/doc/current/components/http_kernel.html#component-http-kernel-event-table

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

相关文章

  • [022] Symfony4 事件机制 Port02

    首先我们先重点关注 kernel.request 事件. 通常在进行 Controller 执行具体的业务代码之前...

  • Android 事件分发机制源码

    Android 事件分发机制源码 Android,事件机制,Android事件分发机制源码 Android Tou...

  • DOM事件概念以及过程

    解释以下概念:事件传播机制、阻止传播、取消默认事件、事件代理 事件传播机制 事件传播机制分为三个阶段 事件捕获阶段...

  • DOM事件概念以及过程

    解释以下概念:事件传播机制、阻止传播、取消默认事件、事件代理 事件传播机制 事件传播机制分为三个阶段 事件捕获阶段...

  • javascript事件

    解释以下概念:事件传播机制、阻止传播、取消默认事件、事件代理。 事件传播机制事件传播机制分为三个阶段 事件捕获阶段...

  • 事件、注册登录、Tab 切换效果

    解释以下概念:事件传播机制、阻止传播、取消默认事件、事件代理 事件传播机制 事件传播机制分为三个阶段:1、捕获阶段...

  • --

    react事件机制 react基于浏览器原生事件机制,封装了一套自己的事件机制,包括事件注册、事件合成、冒泡和捕获...

  • node事件机制

    什么是node事件机制 node都说是事件驱动的IO,那到底什么是node中的事件机制呢 在node事件机制,需要...

  • 4.2View相关-事件分发机制详解

    事件分发机制 为什么会有事件分发机制 三个重要的事件分发的方法 事件分发流程 1.为什么会有事件分发机制 andr...

  • jQuery事件

    1.事件机制jQuery的事件机制,指的是:jQuery对JavaScript操作DOM事件的封装,包括了:事件绑...

网友评论

      本文标题:[022] Symfony4 事件机制 Port02

      本文链接:https://www.haomeiwen.com/subject/xvxopqtx.html