Symfony2 學習筆記之內部構件

huidaoli發表於2014-08-06



Symfony2內部是怎樣工作的以及我們如何來擴充套件它呢?
從外部整體上看,symfony2程式碼是由許多獨立的層構成,每一層都是建立在前一層基礎之上。其中,自動載入時不受框架直接管理的,它完全是在UniversalClassLoader類和src/autoload.php檔案的幫助下獨立完成的。

HttpFoundation 元件
最深層次的是HttpFoundation元件,它提供了處理HTTP所需的主要物件。是一個對一些PHP函式和變數的物件導向抽象。
包括:
Request 類,抽象了PHP中主要的全域性變數$_GET,$_POST,$_COOKIE,$_FILES 和 $_SERVER。
Response類,抽象類一些PHP函式比如header(), setcookie()和echo();
Session 類和SessionStorageInterface介面則是抽象了Session管理Session_*()函式。

 

HttpKernel 元件:
在HttpFoundation元件之上建立的一個元件,它處理HTTP的動態部分。它是為了能夠通過標準的方式來處理request,而對Request和Response物件的一個最小封裝。同時它提供了一些擴充套件點和工具,讓它成為建立一個web框架的最理想的開始點。

另外,Dependency Injection元件和強大的外掛系統bundles讓它增加了可配置性和擴充套件性。

 

FrameworkBundle Bundle
FrameworkBundle bundle 是一個bundle,它是構成輕量級快速MVC框架的主要元件和類庫。

 

Kernel
HttpKernel類是Symfony2的中心類,它負責處理客戶端請求。它的主要任務就是把Request物件轉換成Response物件。每個Symfony2核心實現HttpKernelInterface介面。

function handle(Request $request,$type=self::MASTER_REQUEST,$catch=true)

 


Controller
Kernel依靠Controller來吧Request轉換為Response。 Controller可以是任何有效的PHP呼叫。Kernel委託選擇哪個Controller應該被執行給一個ControllerResolverInterface介面的實現者。

public function getController(Request $request);
public function getArguments(Request $request,$controller);

其中,getController()方法返回一個和給定的Request相對於的Controller(一個PHP呼叫)。ControllerResolver的預設實現是查詢Request的一個_controller屬性,它的值是一個class::method 格式的字串。比如Bundle\BlogBundle\PostController::indexAction 。預設的實現使用RouterListener來定義Request的屬性 _controller。getArguments()方法返回一個輸入引數陣列來傳遞給Controller呼叫。預設實現會根據Request屬性自動的獲取這些輸入引數。

從Request屬性中匹配Controller方法的輸入引數:Symfony2對於每一個方法的輸入引數都會是這從Request中查詢其同名屬性,如果沒有定義,就會取該輸入引數的預設值。

// Symfony2 從Request屬性中查詢 'id' 屬性(強制)
// 和一個'admin' 屬性 (可選)
public function showAction($id, $admin = true)
{
       // ...
}

處理請求:handle()方法需要一個Request引數並且永遠都必須返回一個Response。要轉換Request,handle()需要依靠一個分析器和一個事件通知順序鏈。
1. 在處理任何事情之前,kernel.request事件將被通知 --如果一個監聽者返回了一個Response,那麼它會直接跳至第8步。
2. 分析器被呼叫來判斷哪個Controller將被執行。
3. kernel.controller事件監聽器現在開始處理Controller呼叫(改變它,封裝它...)
4. Kernel檢查Controller是否是一個合法可呼叫的PHP回撥。
5. 分析器被呼叫來決定傳遞給controller的引數
6. Kernel呼叫Controller
7. 如果Controller沒有返回Response物件,kernel.view事件監聽器會把Controller的返回值轉換成一個Response。
8. kernel.response事件監聽器開始處理Response(內容和頭部);
9. Response物件被返回。

如果在這個過程中有一個異常被丟擲,kernel.exception就會被通知,監聽器就把異常轉換為一個Response,之後kernel.response事件就會被通知,如果沒能轉換,該異常就會被丟擲。

如果你不想異常被捕獲,你可以通過傳遞一個false作為第三個引數到handle()方法,來關閉kernel.exception事件。

 

內部請求
在處理一個主請求的任何時候,子請求都可以被處理。你可以傳遞一個請求型別到handle()方法,作為它的第二個引數。

HttpKernelInterface::MASTER_ReQUEST;
HttpKernelInterface::SUB_REQUEST

這些型別會根據需要傳遞給所有的事件和監聽器。


事件
Kernel丟擲的每一個事件都會是KernelEvent類的子類。這就意味著每個事件都可以訪問相同的基礎資訊。
getRequestType() 返回請求的型別(HttpKernelInterface::MASTER_REQUEST 或者 HttpKernelInterface::SUB_REQUEST;
getKernel() 返回處理請求的Kernel
getRequest() 返回一個當前被處理的請求

getRequestType()方法允許監聽器知道請求的型別。比如,如果一個監聽器必須是主請求才能啟用它,你可以把該程式碼新增到你監聽器方法的開頭:

use Symfony\Component\HttpKernel\HttpKernelInterface;
if(HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()){
      //立刻返回
      return;
}

 

kernel.request 事件
事件類:GetResponseEvent

該事件的目標是立刻返回一個Response物件或者建立一個在事件結束後Controller可以呼叫的變數。任何監聽器都可以通過event的setResponse()方法返回一個Response物件,當有Response物件被返回時,其它的監聽器就不能在被呼叫了。

FrameworkBundle使用事件通過RouterListener來釋出一個_controller 請求屬性。
RequestListener 使用一個RouterInterface物件匹配Request,決定哪個Controller的名字會被儲存到_controller的請求屬性裡。


kernel.controller 事件:
事件類: FilterControllerEvent

FrameworkBundle不會使用該事件,但是該事件可以被作為一個修改要執行的controller的一個入口點。

複製程式碼
use Symfony\Component\Httpkernel\Event\FilterControllerEvent;

public function onKernelController(FilterControllerEvent $event)
{
        $controller = $event->getController();
         //...

        // 此處controller可以被該換成任何PHP可回撥函式
        $event->setController($controller);
}
複製程式碼

 

 

kernel.view 事件
事件類:GetResponseForControllerResultEvent

FrameworkBundle也不會使用該事件,但是它被用來實現一個檢視子系統。該事件只有當Controller不能返回一個Response物件時才被呼叫。它的目的就是把其他型別的返回值轉換成一個Response。

Controller的返回值可以通過getControllerResult方法訪問:

複製程式碼
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;

public function onKernelView(GetResponseForControllerResultEvent $event)
{
        $val = $event->getControllerResult();
        $response = new Response();
        //通過返回值自定義化Response
        $event->setResponse($response);
}
複製程式碼

 


kernel.response 事件
事件類:FilterResponseEvent

該事件的目的是允許其它系統在Response物件被建立後對它進行修改或者替換。

public function onKernelResponse(FilterResponseEvent $event)
{
       $response = $event->getResponse();
       //修改Response物件
}

FrameworkBundle註冊了許多的監聽器:

        ProfilerListener 從當前請求中搜集資料
        WebDebugToolbarListener 注入Web 除錯工具條
        ResponseListener 基於請求的格式來為Response設定Content-type
        EsiListener 當Response需要解析ESI標籤時,向其新增一個Surrogate-Control HTTP頭。

 

kernel.exception 事件:
事件類:GetResponseForExceptionEvent
     FrameworkBundle註冊一個ExceptionListener把請求定向到一個給定Contoller。這個事件的監聽器可以建立和設定一個Response物件,建立設定一個新的Exception物件或者什麼都不做。

複製程式碼
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;

public function onKernelException(GetResponseForExceptionEvent $event)
{
        $exception = $event->getException();
        $response = new Response();
        // 基於捕獲的異常建立一個Response物件
        $event->setResponse($response);

        // 你可以建立一個新的異常代替原有的
        // $exception = new \Exception('Some special exception');
        // $event->setException($exception);
}
複製程式碼

 

總結思考:我們瞭解了Symfony2內部的主要部件和一些主要的事件介面,我們可以在各個事件介面定義相應的監聽器處理,來對請求處理過程進行干預操作。

 

參考URL:http://symfony.com/doc/current/book/internals.html

相關文章