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