zoukankan      html  css  js  c++  java
  • Symfony2源码分析——启动过程2

      文章地址:http://www.hcoding.com/?p=46

      上一篇分析Symfony2框架源码,探究Symfony2如何完成一个请求的前半部分,前半部分可以理解为Symfony2框架为处理请求做准备工作,包括container生成、缓存、bundls初始化等一些列准备工作(Symfony2源码分析——启动过程1)。而这一篇讲的是Symfony2如何根据请求的数据生成Response对象,向客户端返回响应数据。

      在分析前需要了解Symfony2的事件驱动机制:Symfony2事件驱动

      言归正传,Symfony2请求的工作流程其实是Symfony2内核的事件驱动完成的,下面是Symfony2框架定义好的内核事件:

    final class KernelEvents
    {
        /**
         * The REQUEST event occurs at the very beginning of request
         * dispatching
         *
         * This event allows you to create a response for a request before any
         * other code in the framework is executed. The event listener method
         * receives a SymfonyComponentHttpKernelEventGetResponseEvent
         * instance.
         *
         * @var string
         *
         * @api
         */
        const REQUEST = 'kernel.request';
    
        /**
         * The EXCEPTION event occurs when an uncaught exception appears
         *
         * This event allows you to create a response for a thrown exception or
         * to modify the thrown exception. The event listener method receives
         * a SymfonyComponentHttpKernelEventGetResponseForExceptionEvent
         * instance.
         *
         * @var string
         *
         * @api
         */
        const EXCEPTION = 'kernel.exception';
    
        /**
         * The VIEW event occurs when the return value of a controller
         * is not a Response instance
         *
         * This event allows you to create a response for the return value of the
         * controller. The event listener method receives a
         * SymfonyComponentHttpKernelEventGetResponseForControllerResultEvent
         * instance.
         *
         * @var string
         *
         * @api
         */
        const VIEW = 'kernel.view';
    
        /**
         * The CONTROLLER event occurs once a controller was found for
         * handling a request
         *
         * This event allows you to change the controller that will handle the
         * request. The event listener method receives a
         * SymfonyComponentHttpKernelEventFilterControllerEvent instance.
         *
         * @var string
         *
         * @api
         */
        const CONTROLLER = 'kernel.controller';
    
        /**
         * The RESPONSE event occurs once a response was created for
         * replying to a request
         *
         * This event allows you to modify or replace the response that will be
         * replied. The event listener method receives a
         * SymfonyComponentHttpKernelEventFilterResponseEvent instance.
         *
         * @var string
         *
         * @api
         */
        const RESPONSE = 'kernel.response';
    
        /**
         * The TERMINATE event occurs once a response was sent
         *
         * This event allows you to run expensive post-response jobs.
         * The event listener method receives a
         * SymfonyComponentHttpKernelEventPostResponseEvent instance.
         *
         * @var string
         */
        const TERMINATE = 'kernel.terminate';
    
        /**
         * The FINISH_REQUEST event occurs when a response was generated for a request.
         *
         * This event allows you to reset the global and environmental state of
         * the application, when it was changed during the request.
         *
         * @var string
         */
        const FINISH_REQUEST = 'kernel.finish_request';
    }
    View Code

      我们可以编写事件监听器,监听相应的内核事件,在Symfony2触发该事件的时候,相应的事件监听器就会执行。监听和唤醒形象的描述,就像,你(事件监听器)参加校运会,去大会(Symfony2)登记(监听)参加50米短跑(事件),当50米短跑比赛开始了(事件被触发),那你就奔跑吧(监听器执行,其实就是一个执行函数,函数完成什么工作就取决于你的需求了),少年。

      Symfony2的内核事件处理流程大部分工作都在HttpKernel::handleRaw方法中:

     1     private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
     2     {
     3         $this->requestStack->push($request);
     4 
     5         // request
     6         // 初始化事件,事件对象会被传递给监听器,所以事件可以说是一个信息的载体,事件内存放着监听器感兴趣的数据。
     7         $event = new GetResponseEvent($this, $request, $type);
     8         // 触发kernel.request事件,后续详细讲解EventDispatcher::dispatch方法的实现,
     9         // 这里我们需要知道的是,dispatcher把$event传递给所有监听了kernel.request事件的监听器,监听器将会执行。
    10         // kernel.request事件发生在controller执行之前,我们可以在这一步奏完成路由解析等为controller执行提供准备数据,
    11         // 在这个过程允许我们直接生成Response对象,向客户端输出数据,那么controller就不会被执行了。
    12         $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);
    13 
    14         // 如果我们在kernel.request事件生成了Response对象(响应数据),那么就跳过kernel.controller、kernel.view事件、
    15         // controller也会被跳过,直接执行kernel.response事件。
    16         if ($event->hasResponse()) {
    17             return $this->filterResponse($event->getResponse(), $request, $type);
    18         }
    19 
    20         // load controller
    21         // 根据路由规则返回 一个对象或者数组或者字符串 ,如果$controller是一个数组,$controller[0]是存放的是要执行的controller对象,
    22         // $controller[0]存放的是controller对象执行的方法,即action,方法的参数没有保存在$controller数组中;
    23         // 如果$controller是对象,那么该对象就实现了__invoke 方法;
    24         // 如果$controller是字符串,那么$controller就是要运行的函数的函数名。
    25         // 图2是$controller的一个var_dump例子
    26         if (false === $controller = $this->resolver->getController($request)) {
    27             throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
    28         }
    29 
    30         $event = new FilterControllerEvent($this, $controller, $request, $type);
    31         // 触发kernel.controller事件,这个事件发生在controller执行前。我们可以通过监听这个事件在controller执行前修改controller,
    32         // 或者完成一些动作。
    33         $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
    34         $controller = $event->getController();
    35 
    36         // controller arguments
    37         // 从request对象中获取controller方法的参数
    38         $arguments = $this->resolver->getArguments($request, $controller);
    39 
    40         // call controller
    41         // 执行controller
    42         $response = call_user_func_array($controller, $arguments);
    43 
    44         // view
    45         // 如果$response不是Response对象,那么kernel.view事件就会触发,监听kernel.view事件的监听器通过$response值生成Response对象。
    46         if (!$response instanceof Response) {
    47             $event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
    48             $this->dispatcher->dispatch(KernelEvents::VIEW, $event);
    49 
    50             if ($event->hasResponse()) {
    51                 $response = $event->getResponse();
    52             }
    53 
    54             if (!$response instanceof Response) {
    55                 $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));
    56 
    57                 // the user may have forgotten to return something
    58                 if (null === $response) {
    59                     $msg .= ' Did you forget to add a return statement somewhere in your controller?';
    60                 }
    61                 throw new LogicException($msg);
    62             }
    63         }
    64 
    65         // 触发kernel.response事件,在向客户端输出Response对象前,我们可以对Response对象进行修改,
    66         // 例如修改response头部,设置缓存、压缩输出数据等。
    67 
    68         // 接着触发kernel.finish_request事件,把当前请求从请求栈中弹出,当前请求就完成。
    69         return $this->filterResponse($response, $request, $type);
    70 
    71         // 千万别忘记了,filterResponse执行完后,Symfony2内核事件处理流程还有最后一步,位于app_dev.php[app.php]最后一行,
    72         // $kernel->terminate($request, $response);这个方法触发kernel.terminate事件,此时,Symfony2已经响应了客户端的请求,
    73         // 向客户端输出了Response对象。监听kernel.terminate事件的监听器,主要是为了完成一些耗时的操作,操作的结果不需要返回给
    74         // 客户端的,例如邮件发送、图片压缩等等。
    75         // 到这里,Symfony2的整个流程就走完了。
    76     }
    HttpKernel::filterResponse方法和HttpKernel::finishRequest方法:
     1     private function filterResponse(Response $response, Request $request, $type)
     2     {
     3         $event = new FilterResponseEvent($this, $request, $type, $response);
     4 
     5         $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event);
     6 
     7         $this->finishRequest($request, $type);
     8 
     9         return $event->getResponse();
    10     }
    11 
    12     /**
    13      * Publishes the finish request event, then pop the request from the stack.
    14      *
    15      * Note that the order of the operations is important here, otherwise
    16      * operations such as {@link RequestStack::getParentRequest()} can lead to
    17      * weird results.
    18      *
    19      * @param Request $request
    20      * @param int     $type
    21      */
    22     private function finishRequest(Request $request, $type)
    23     {
    24         $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type));
    25         $this->requestStack->pop();
    26     }
    View Code

    图2

    Symfony2框架的事件分发机制的核心代码:

     1     public function dispatch($eventName, Event $event = null)
     2     {
     3         if (null === $event) {
     4             $event = new Event();
     5         }
     6 
     7         $event->setDispatcher($this);
     8         $event->setName($eventName);
     9 
    10         if (!isset($this->listeners[$eventName])) {
    11             return $event;
    12         }
    13 
    14         // $eventName即:KernelEvents::REQUEST、KernelEvents::CONTROLLER、KernelEvents::VIEW、KernelEvents::RESPONSE、KernelEvents::TERMINATE等
    15         // getListeners返回所有监听$eventName事件的监听器
    16         $this->doDispatch($this->getListeners($eventName), $eventName, $event);
    17 
    18         return $event;
    19     }
    20 
    21     protected function doDispatch($listeners, $eventName, Event $event)
    22     {
    23         // 监听器执行
    24         foreach ($listeners as $listener) {
    25             call_user_func($listener, $event, $eventName, $this);
    26             // 如果其中一个监听器把$event的propagationStopped属性设置为true,那么表示$eventName这一事件终止执行,
    27             // 事件不会往$listeners里尚未执行的监听器传递该事件。
    28             if ($event->isPropagationStopped()) {
    29                 break;
    30             }
    31         }
    32     }
  • 相关阅读:
    改进动物园
    异常动手脑总结
    代码大全2 阅读笔记
    抽象和接口
    Appium+python自动化2-启动百度app
    Appium+python自动化1-环境搭建
    python之图像识别
    python之栈和队列
    python之语音识别(speech模块)
    使用Python计算IP、TCP、UDP校验和
  • 原文地址:https://www.cnblogs.com/szuyuan/p/4048808.html
Copyright © 2011-2022 走看看