Symfony2源碼分析——啓動過程2

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

  上一篇分析Symfony2框架源碼,探究Symfony2如何完成一個請求的前半部分,前半部分能夠理解爲Symfony2框架爲處理請求作準備工做,包括container生成、緩存、bundls初始化等一些列準備工做(Symfony2源碼分析——啓動過程1)。而這一篇講的是Symfony2如何根據請求的數據生成Response對象,向客戶端返回響應數據。html

  在分析前須要瞭解Symfony2的事件驅動機制:Symfony2事件驅動api

  言歸正傳,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 Symfony\Component\HttpKernel\Event\GetResponseEvent
     * 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 Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
     * 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
     * Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
     * 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
     * Symfony\Component\HttpKernel\Event\FilterControllerEvent 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
     * Symfony\Component\HttpKernel\Event\FilterResponseEvent 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
     * Symfony\Component\HttpKernel\Event\PostResponseEvent 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方法中:app

 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框架的事件分發機制的核心代碼:ide

 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     }
相關文章
相關標籤/搜索