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內部的主要部件和一些主要的事件介面,我們可以在各個事件介面定義相應的監聽器處理,來對請求處理過程進行干預操作。