文章來自於:http://bbs.phpchina.com/thread-268362-1-1.htmlphp
ZF2已經發布,與ZF1相比,MVC這一模塊內部的實現機制可謂截然不同,許多用過ZF1的PHPer剛接觸ZF2會有種無所適從的感受,同時ZF2的官方手冊也不是很詳盡,許多細節只有經過看源代碼才能搞懂,而ZF2中又大量使用了各種設計模式,使得對設計模式不熟悉的開發者閱讀源代碼的時候更是一頭霧水。下面我就簡單的介紹一下ZF2中MVC的主要流程。
ZF2的MVC模塊主要採用了Service Locator與事件驅動的設計模式。下面先簡單介紹下這2部份內容。
一、Service Locator
在ZF2中,新增了一個Zend\ServiceManager組件,該組件實現了Service Locator模式。在ZF2中,任何的類庫,擴展等資源都統一被視爲服務對象,並在ServiceManager組件中註冊。當應用程序須要某個功能,例如Zend\Log時,推薦的作法是在ServiceManager中獲取,而不是像傳統方式那樣,include類文件以後再new一個對象出來。這種方式的好處在於解除了服務調用者與服務提供者之間的耦合,做爲一名用戶,不須要知道Zend\Log位置是在哪裏,是如何產生的,只須要知道用Zend\ServiceManager::get()這種方式來取得它就好了。
要在ServiceManager中註冊服務對象,有下面幾種方法:
直接對象:調用setService方法,能夠直接把某個對象註冊爲一個服務對象。html
01
02
03
|
$serviceManager
->setService(
'Log'
,
new
Zend\Log\Logger());
$logger
=
$serviceManager
->get(
'Log'
);
$serviceManager
->setService(
'settings'
,
array
(
'password'
=>
'123456'
));
|
延遲對象:調用setInvokableClass方法,其實就是先註冊一個類的名字,當須要使用該服務對象時,ServiceManager實例化一個對象給你。數據庫
01
02
|
$serviceManager
->setInvokableClass(
'Log'
,
'Zend\Log\Logger'
);
$logger
=
$serviceManager
->get(
'Log'
);
|
工廠對象:調用setFactory方法,有許多服務對象的產生比較複雜,好比根據配置文件的內容來產生對象等等,這時就須要有個專門的「工廠」來產生服務對象。工廠能夠是匿名函數,也能夠是實現了Zend\ServiceManager\FactoryInterface的類或對象。
bootstrap
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
use
Zend\ServiceManager\FactoryInterface;
use
Zend\ServiceManager\ServiceLocatorInterface;
class
LogFactory
implements
FactoryInterface
{
public
function
createService(ServiceLocatorInterface
$serviceLocator
)
{
return
new
Zend\Log\Logger();
}
}
// 實現了Zend\ServiceManager\FactoryInterface
$serviceManager
->setFactory(
'Log1'
,
new
LogFactory());
$serviceManager
->setFactory(
'Log2'
,
'LogFactory'
);
$logger
=
$serviceManager
->get(
'Log1'
);
// 匿名函數
$serviceManager
->setFactory(
'service3'
,
function
() {
return
new
\stdClass(); });
|
抽象工廠對象:若是要取得一個名字不存在的服務對象時,ServiceManager會去尋找註冊的抽象工廠,抽象工廠實現了Zend\ServiceManager\AbstractFactoryInterface,若是有某個抽象工廠的canCreateServiceWithName方法返回真,則ServiceManager返回該抽象工廠的createServiceWithName方法所產生的對象。設計模式
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
use
Zend\ServiceManager\ServiceLocatorInterface;
use
Zend\ServiceManager\AbstractFactoryInterface;
class
MyAbstractFactory
implements
AbstractFactoryInterface
{
public
function
canCreateServiceWithName(ServiceLocatorInterface
$serviceLocator
,
$name
,
$requestedName
)
{
// this abstract factory only knows about 'foo' and 'bar'
return
$requestedName
===
'foo'
||
$requestedName
===
'bar'
;
}
public
function
createServiceWithName(ServiceLocatorInterface
$serviceLocator
,
$name
,
$requestedName
)
{
$service
=
new
\stdClass();
$service
->name =
$requestedName
;
return
$service
;
}
}
$serviceManager
->addAbstractFactory(
'MyAbstractFactory'
);
var_dump(
$serviceManager
->get(
'foo'
)->name);
// foo
var_dump(
$serviceManager
->get(
'bar'
)->name);
// bar
var_dump(
$serviceManager
->get(
'baz'
)->name);
// exception! Zend\ServiceManager\Exception\ServiceNotFoundException
|
服務對象的初始化:ServiceManager還提供了對服務對象的額外初始化功能,調用addInitializer()方法能夠註冊對象初始化器,每當調用ServiceManager的get()方法返回服務對象前,都會對該對象調用全部註冊的初始化器,該功能通常用於對第三方開發的服務對象的初始化。
看到這裏,若是有熟悉Java的,特別是Spring框架的同窗,可能會發現這ServiceManager與Spring中的Ioc容器有點類似。沒錯,其實二者都是爲了實現一個目的:把對象的生產者與對象的使用者之間的耦合性降到最低。Spring中採用的是依賴注入的模式,而ZF2中本來也打算使用用依賴注入的模式,還專門開發了Zend\Di組件,卻在最後一個測試版中換成了ServiceLocator模式。但不管採用哪一種模式,最終的目的與產生的效果仍是同樣的:即下降耦合性。咱們在使用ZF2的時候也要注意這一點:對象的產生不該該是由對象的使用者決定的,使用者只是選擇採用哪個對象而已。
二、事件驅動
在ZF2中,大量使用了事件驅動的模式。在ZF2中,提供了一個新的Zend\EventManager組件來實現事件驅動。打開Zend\EventMan ager目錄,能夠看到不少都是一些接口(Interface),核心的就是兩個文件,Event.php和EventManager.php,望文生義,一個是事件類(Event),一個是事件管理類(EventManager)。
先說說EventManager類,EventManager類主要負責綁定(attach)事件、解除(detach)事件、觸發事件(trigger),在EventManager類內部,維護着一個事件數組,事件數組維護者事件名稱與事件處理函數的一對多的對應關係,也就是說一個事件名稱能夠綁定多個事件處理對象(函數),當事件被觸發後,按照綁定順序,依次執行所綁定的事件處理對象(函數),事件處理對象(函數)也被稱爲監聽器(Listener)。
舉個例子可能來理解起來比較清楚:有個論壇用戶發文章的功能,每當保存數據到數據庫以前,會觸發一個名爲preSaveContent的事件,咱們能夠爲該事件綁定多個處理函數,好比檢查用戶權限的函數,過濾敏感字符的函數,判斷IP的函數等等,這些函數會以綁定順序依次執行。
再說說Event類,Event類實例化後就是Event對象,該對象保存了事件的名稱,事件發生時的上下文,須要傳遞給事件處理函數的一些參數。值得一提的是stopPropagation方法,該方法阻止了事件的繼續處理,也就是說執行了該方法後,事件隊列中後面的處理函數將不會執行。
在ZF2中預約義了一些不一樣的事件對象,如ModuleEvent、MvcEvent、ViewEvent、SendResponseEvent等等,他們都繼承於Zend\EventManager\Event。這些不一樣的事件對象所起的做用實際上是同樣的,都是在事件驅動的工做流程中把事件發生時的上下文信息傳遞給監聽器(Listener),不一樣之處在於事件發生時的邏輯環境。任何程序在邏輯上老是會有不一樣的執行階段的,在不一樣階段的上下文環境是不一樣的,若是用一個事件對象來貫穿全部的執行階段,必然會在該對象上附加全部階段的上下文信息,從而致使該對象的臃腫,程序結構也不清晰。所以好的設計方法就是在不一樣的邏輯環境中,使用不一樣的事件對象。
下面介紹一下ZF2中MVC的啓動過程,在ZF2的官網中,提供了一個例子程序:http://framework.zend.com/manual/2.2/en/user-guide/skeleton-application.html ,這裏就用這個例子做爲說明對象。
把官網的示例程序導入Zend Studio後,是下圖的結構:
其中,vendor目錄通常存放第三方開發的或是本身開發的通用的模塊(Module),而module目錄通常存放當前項目的模塊(Module)。在ZF2中,每一個Module就是一個徹底獨立的功能模塊,能夠擁有本身的配置文件、語言文件等等。
與ZF1同樣,入口文件是public目錄下的index.php文件,該文件一共就3行代碼:數組
01
02
03
04
05
06
07
08
09
10
11
12
|
<?php
/**
* This makes our life easier when dealing with paths. Everything is relative
* to the application root now.
*/
chdir
(dirname(__DIR__));
// Setup autoloading
require
'init_autoloader.php'
;
// Run the application!
Zend\Mvc\Application::init(
require
'config/application.config.php'
)->run();
|
前兩行是設置一個PHP類的自動加載器(autoloading),第三行纔是真正的MVC的啓動代碼。
ZF2在邏輯上把一個PHP項目看做一個服務端程序,而不是分散的頁面,因此ZF2在設計MVC框架的時候,在邏輯上把運行過程分爲兩個步驟:一、各模塊(Module)以及服務對象的初始化;二、程序運行。
一、初始化
初始化主要功能就是讀取配置文件,根據配置文件中的內容生成一個個服務對象並進行適當的初始化設置、註冊各種事件並綁定事件處理對象(函數)。這裏有一點要提一下,官方推薦的實例佈局中,能夠看到配置文件分散在許多地方,其中每一個模塊(Module)下面都有一個config目錄,用來存放模塊本身的配置文件,其內容稱爲模塊配置信息(ModuleConfig);而在項目根目錄下,有一個config目錄,存放的是與整個項目結構(諸若有哪些模塊、模塊存放在哪裏等等)有關的配置文件,其稱爲應用程序配置信息(ApplicationConfig);而autoload目錄下存放的是與項目版本,環境有關的配置文件,他們會覆蓋module目錄下配置文件的內容,其內容稱爲全局配置信息(GlobConfig),其中goloal.php通常存放與發佈的版本有關的配置信息,例如版本號等,而local.php通常存放與環境有關,涉及安全方面的一些配置信息,如數據庫的密碼等。
初始化執行的是Zend\Mvc\Application的init()方法,一共五行代碼,完成的工做可很多,咱們一行行看。第1、二行讀取配置文件中service_manager和listerners的內容,這裏先略過,由於示例代碼也沒有用到這兩部分配置,因此咱們這裏也不去關注它們,等對ZF2熟悉了之後,天然就能搞明白了。
安全
01
02
|
$serviceManager
=
new
ServiceManager(
new
Service\ServiceManagerConfig(
$smConfig
));
$serviceManager
->setService(
'ApplicationConfig'
,
$configuration
);
|
如上圖,接下來是產生一個ServiceManager對象,上面說過了,ZF2用到了Service Locator模式,程序中用到的服務,對象甚至全局參數均可以從ServiceManager中獲取,這裏生成的ServiceManager對象能夠稱它爲全局ServiceManager對象(或者稱之爲ServiceLocator對象)。爲了方便在程序中獲取ServiceLocator對象,ZF2提供了一個ServiceLocatorAwareInterface接口,凡是實現了ServiceLocatorAwareInterface接口的類,都能經過調用getServiceLocator()方法來取得ServiceLocator對象。
ServiceManager能夠有許多個,通常按照存放的內容不一樣來分類,例如專門存放數據庫對象的ServiceManager,或專門存儲日誌對象的ServiceManager,而ServiceManager對象自己又能夠保存在全局的ServiceManager對象中。
ZF2中就是這麼作的,一個全局的ServiceManager對象中還保存其餘的如ControllerPluginManager、ViewHelperManager、ValidatorManager之類的ServiceManager對象,就拿ControllerPluginManager來講,它保存的是各類控制器插件,ZF2提供了許多的控制器插件,若是把這些控制器插件對象都保存在全局的ServiceManager對象中,會顯得有點臃腫雜亂,因此在全局的ServiceManager對象中就保存了一個ControllerPluginManager對象,而在ControllerPluginManager對象中保存那些五花八門的控制器插件,獲取的時候,就能夠這麼獲取:mvc
01
|
$serviceManager
->get(
'ControllerPluginManager'
)->get(
'redirect'
);
|
ServiceManager對象初始化的時候,默認註冊了兩個工廠,分別是事件管理器工廠(EventManagerFactory)和模塊管理器工廠(ModuleManageFactory),是在Zend\Mvc\Service\ServiceManagerConfig中註冊的,後面咱們會看到在另外一個類中註冊了許多其餘的服務對象。事件管理器工廠比較簡單,僅僅建立一個事件管理器(EventManager)對象,該對象負責整個MVC流程中全部事件的綁定以及觸發。而模塊管理器工廠比較複雜,在建立模塊管理器(ModuleManager)對象時,還綁定了模塊初始化過程當中的一些默認事件監聽對象(Listener)。
在程序初始化階段,有一個重要的部分就是各模塊(Module)的初始化,模塊(Module)初始化過程當中,有四個事件(模塊初始化事件)會被觸發,依次是:loadModules(開始載入全部模塊),loadModule.resolve(單個模塊開始解析),loadModule(單個模塊開始載入),loadModules.post(全部模塊載入完成),以上這些事件定義在Zend\ModuleManager\ModuleEvent類中。在ZF2中,爲這些事件綁定了許多默認的監聽器(Listener),大多數監聽器的綁定由Zend\ModuleManager\Listener\DefaultListenerAggregate類完成,從名字就可看出,它是一個默認監聽器的聚合類。
另外還綁定了一個Zend\ModuleManager\Listener\ServiceListener監聽器,該監聽器是獨立綁定的,並不包括在DefaultListenerAggregate內。ServiceListener是個比較複雜的監聽器,它主要合併各模塊(Module)的配置信息,並根據配置信息註冊服務對象。ServiceListener監聽器是由ServiceListenerFactory產生的,ServiceListenerFactory在產生ServiceListener監聽器後,還爲它設置了許多默認的與MVC有關的服務對象。那麼爲何這些服務對象不在Zend\Mvc\Service\ServiceManagerConfig類中註冊呢?由於這些對象中用到的許多參數是可配置的,由ServiceListener把配置信息中的相關數據傳遞給它們。
模塊管理器(ModuleManager)負責管理全部的模塊(Module),具體有哪些模塊須要處理,則是在配置文件中寫明的,在application.config.php文件返回的配置信息數組中,有一項名爲modules的配置選項就是告訴模塊管理器有哪些模塊須要被處理。app
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
return
array
(
'modules'
=>
array
(
'Application'
,
'Album'
,
'Blog'
,
),
'module_listener_options'
=>
array
(
'config_glob_paths'
=>
array
(
'config/autoload/{,*.}{global,local}.php'
,
'module_paths'
=>
array
(
'./module'
,
'./vendor'
,
),
),
)
|
每一個模塊下都必須有個Module類,該類用來配置當前模塊。
當ServiceManager對象產生並初始化以後,接下去就該是各模塊(Module)的初始化了:框架
01
|
$serviceManager
->get(
'ModuleManager'
)->loadModules();
|
模塊(Module)的初始化,是由ModuleManager觸發相關事件(上文提到的模塊初始化事件)後完成的。這是ZF2中事件驅動機制的第一次運做,後面將會貫穿程序運行的整個生命週期。
下面看看模塊(Module)的初始化究竟作了哪些事情,首先從ServiceManager中取出模塊管理器(ModuleManager),而後執行loadModules()方法,該方法很簡單,就是觸發loadModules與loadModules.post事件。上面說過,在建立模塊管理器(ModuleManager)的時候,在DefaultListenerAggregate類中爲模塊初始化事件綁定了許多默認的監聽器對象(Listener),因此這裏一旦觸發了這些事件後,相應的監聽器對象(Listener)就開始執行。
loadModules是被觸發的第一個事件,當該事件被觸發後,先註冊一個自動裝載Module類的裝載器(autoload),而後遍歷須要處理的模塊,對每一個模塊執行loadModule()方法。在loadModule()方法中,經過調用loadModuleByName()方法觸發loadModule.resolve事件,該事件只綁定了一個監聽器:ModuleResolverListener,用來實例化每一個模塊下的Module類,隨後觸發loadModule事件。
loadModule事件上綁定了衆多的監聽器對象,用來處理Module類,完成不一樣的任務,這些監聽器在ZF2的官網文檔上介紹了比較詳細,能夠參考一下,點擊查看ZF2官網描述。我這裏就按照執行順序簡單描述一下:
AutoloaderListener:實現模塊本身的類自動加載器。
ModuleDependencyCheckerListener:檢查模塊間的依賴關係。
InitTrigger:執行模塊的一些初始化設置 。
OnBootstrapListener:若是Module類中有onBootstrap方法,則把該方法綁定到MvcEvent::EVENT_BOOTSTRAP事件上,該事件將在MVC引導階段被觸發。
LocatorRegistrationListener:在ServiceManager對象中註冊Module對象。
ConfigListener:把模塊本身的配置信息與全局配置信息合併到一塊兒,全局配置信息覆蓋模塊的配置信息。
最後觸發的是loadModules.post事件,它只綁定了一個監聽器對象:ServiceListener,主要負責根據配置信息註冊服務對象,舉個例子比較容易懂。
ZF2默認在全局服務對象(ServiceLocator)中註冊了一個名爲ControllerLoader的服務對象,該對象也是一個ServiceManager對象,用來存放控制器對象。那麼如何在ControllerLoader中註冊控制器對象呢?ZF2是這麼作的:先在ServiceListener中添加要監控的一些ServiceManager對象,其中就包括ControllerLoader,以下列代碼所示。
01
02
03
04
05
06
|
$serviceListener
->addServiceManager(
'ControllerLoader'
,
'controllers'
,
'Zend\ModuleManager\Feature\ControllerProviderInterface'
,
'getControllerConfig'
);
|
這樣,當loadModule事件被觸發時,ServiceListener就會去執行Module類中的getControllerConfig()方法,並把返回的數據保存起來,當loadModules.post事件被觸發時,ServiceListener又會從全局配置信息中尋找名字爲controllers的那個配置項,把數據也保存起來,最後把上述的全部配置信息合併成一個。最後,ServiceListener會根據配置信息爲ControllerLoader對象建立屬於它的ServiceManager對象。在官方示例中,Application模塊下的module.config.php中有下面這麼一條配置信息,根據這條配置信息,就在ControllerLoader對象中註冊了Application\Controller\IndexController控制器對象。
01
02
03
04
05
|
'controllers'
=>
array
(
'invokables'
=>
array
(
'Application\Controller\Index'
=>
'Application\Controller\IndexController'
),
),
|
當初始化階段完成後,會把全部模塊(Module)目錄下的ModuleConfig與全局的GlobConfig的內容合併成一個數組,GlobConfig會覆蓋ModuleConfig下的同名內容,以Config爲名字保存在ServiceLocator對象中,另外在ServiceLocator對象中還有一條爲ApplicationConfig的內容,保存的是config/application.config.php的內容。
模塊(Module)的初始化,若是用一句話來歸納,就是按照每一個模塊的配置信息,在全局服務對象(ServiceLocator)中註冊各個模塊。當模塊初始化完成後,意味着在程序中全部要用到的服務對象都已準備就緒,接下來就該是這些模塊開始工做了。
二、程序運行
當模塊(Module)初始化結束後,就能夠進入程序的正式運行階段了:上文提到過,ZF2在邏輯上把一個Web項目看做一個服務端的程序,而不是各個分散的頁面,所以ZF2提供了一個Zend\Mvc\Application類來實現這個邏輯,這樣一來,各種的HTTP請求在ZF2中就被看做是Application中接收到的一個消息而已。咱們接着往下看:
01
|
$serviceManager
->get(
'Application'
)->bootstrap(
$listeners
);
|
上述代碼先取出一個已經註冊過的Application對象,該對象由Zend\Mvc\Service\ApplicationFactory建立。在Application對象建立的時候,其內部建立了一個Request對象和一個Response對象,分別表示Application從外界收到的請求與發出的迴應。而後執行bootstrap()方法。
bootstrap,中文意思是引導,顧名思義,進入到引導階段,說明程序立刻要運行了。由於程序運行是基於MVC模式的,所以引導階段的主要任務就是爲MVC的正常工做作準備。ZF2中的MVC模式按照生命週期看,分爲四個部分:路由解析(route resolve)、調度(dispatch)、渲染視圖(render view)、發送響應(send response),ZF2中也爲之定義了相關的事件名稱。
在模塊初始化階段,事件對象爲ModuleEvent,那麼在MVC工做階段,事件對象就是MvcEvent了,而MvcEvent對象的初始化,天然就放在引導階段了。在bootstrap()方法中,先爲MVC的相關事件綁定監聽器,而後產生MvcEvent對象並設置了上下文環境以及Request、Response、Router三個對象,最後觸發在MvcEvent對象上的MvcEvent_EVENT_BOOTSTRAP事件,下面就詳細描述一下這些動做內部的過程。
1) 綁定監聽器
以前已經屢次強調,ZF2是基於事件驅動的,因此MVC的整個工做流程就是觸發不一樣階段的MVC事件,而後由系統回調綁定的監聽器(事件處理方法)。ZF2提供了貫穿整個MVC過程的默認的監聽器,這些監聽器被集合在四個類中,分別是RouteListener、DispatchListener、ViewManager、SendResponseListener。因爲MVC事件中綁定了許多的監聽器,爲了方便你們瞭解,ZF2的官方文檔中詳細羅列了這些事件綁定的監聽器以及觸發條件和觸發位置,具體內容請看:http://framework.zend.com/manual/2.2/en/modules/zend.mvc.mvc-event.html
RouteListener:爲route事件綁定了一個監聽器——>RouteListener::onRoute(),該監聽器用來解析HTTP請求中的URL地址(嚴格說是REQUEST_URI域),若是解析失敗觸發dispatch.error事件。
DispatchListener:爲dispatch事件綁定了一個監聽器——>DispatchListener::onDispatch(),該監聽器根據解析後的URL地址信息找到對應的控制器(Controller)對象,而後執行對應的動做方法(Action)。
ViewManager:ZF2中的視圖是個至關複雜的組件,涉及到一堆概念:變量的容器(Variables Containers)、視圖模型(View Models)、視圖助手(Helper)、模板(Template)、渲染策略(Render Strategy)、腳本解析(Reslover)、響應策略(Response Strategy),ZF2還專門定義了一個視圖事件(ViewEvent)的類來參與對視圖部分的事件處理。所以在ViewManager類中爲各種事件綁定了衆多的與視圖處理有關的監聽器,具體做用到了後面視圖部分會專門敘述,這裏就不詳說了。
SendResponseListener:爲finish事件綁定了一個監聽器——>SendResponseListener::sendResponse(),該監聽器用來產生ResponseEvent對象,並在該對象上觸發一系列與發送相應請求(Response)有關的事件。
2) 觸發MvcEvent上的bootstrap事件
與MVC事件相關的監聽器綁定完畢後,開始觸發MvcEvent對象上的第一個事件MvcEvent_EVENT_BOOTSTRAP。該事件觸發後,會去檢查各個模塊(Module)下Module類中的onBootstrap()方法,若是有就執行。另外在ViewManager中也有響應該事件的onBootstrap()方法,主要用來爲其餘事件綁定監聽器,用來處理視圖方面的內容。
當引導程序執行完畢後,意味着與MVC相關的組件都已準備就緒了,以後調用Zend\Mvc\Application::run()方法,開始運行程序。run()方法其實很簡單,主要就是觸發了四個事件:MvcEvent::EVENT_ROUTE、MvcEvent::EVENT_DISPATCH、MvcEvent::EVENT_RENDER、MvcEvent::EVENT_FINISH。綁定在這些事件上的監聽器會負責完成MVC的整個流程。
首先觸發的是MvcEvent::EVENT_ROUTE事件,從名字上就能夠看出,該事件觸發後,會完成路由解析的工做。該事件綁定了兩個監聽器(見上圖),主要功能集中在Zend\Mvc\RouteListener這個監聽器上。在監聽器中,先從MvcEvent對象中取出路由對象(router),隨後執行match()方法,若是當前的HTTP請求中的REQUEST_URI域(就是請求的URL地址)有與之相配的路由,則返回一個Zend\Mvc\Router\RouteMatch對象,並把它在MvcEvent對象中保存一份,以方便後續程序的使用。其中match()方法咱們能夠不用去關心,只要知道路由成功匹配後會返回一個RouteMatch對象就好了。重點是路由對象(router)的產生,路由對象(router)是Zend\Mvc\Router\Http\TreeRouteStack的實例,在引導階段(bootstrap)產生,與其餘大多數的對象同樣,它也是一個服務對象(註冊在Service Locator中,由Zend\Mvc\Service\RouterFactory產生), 因此也是從全局的ServiceManger對象中獲取它。
在RouterFactory中,咱們能夠發現它會去配置信息中尋找一個名爲route_plugin的項,若是沒提供該配置項的話,默認會把Zend\Mvc\Router\RoutePluginManager做爲該項的內容。鑑於這個route_plugin所指向的內容在路由中佔據了重要地位,所以先在這裏介紹一下它,明白了它所起的做用,就等於瞭解了路由的內部實現機制。
路由,說到底就是一種映射關係,在ZF2中定義了一些通用的映射方式,並封裝成了類,稱之爲route,一個route其實就是一個路由協議(這裏請注意與路由對象——router的區別),路由對象(router)就是用這些route來與HTTP請求中的URL地址做匹配的,而這些route在路由對象(router)內部是由一個名爲routePluginManager的對象負責產生的,該routePluginManager對象就由route_plugin所指定,必須爲Zend\Mvc\Router\RoutePluginManager的子類。仔細看下RoutePluginManager的繼承關係,發現它是Zend\ServiceManager\ServiceManager的子類,這下明白了,所謂的RoutePluginManager,其實就是一個服務對象管理器,而route則是註冊在RoutePluginManager的服務對象,固然RoutePluginManager自己又是在全局服務對象(Service Locator)中註冊的。有了routePluginManager,路由對象(router)就能夠根據配置文件中的信息,生成一個個route對象了。
定義route對象的配置信息通常以下:
咱們能夠看到,配置信息很是複雜,這是不少人的第一印象,可是當咱們理解了這些配置信息背後的含義以後,會明白實際上是頗有規律可循的,也是用起來至關簡單的。咱們從第二行開始講起,第二行的router表示指向的數組是路由配置信息,這個router是默認名字,不可改變。第三行的routes表示指向的數組是路由表項的配置信息,這個routes也是默認名字,不可改變。路由表項能夠有任意個,每個描述了一個路由規則,咱們這裏定義了兩個路由表項,分別取名爲home與application。
home路由表項的配置比較簡單,共有2個參數——type與options,type參數指定了該路由表項使用的是哪一個路由協議,路由協議必須是在RoutePluginMnager中註冊過的,而options參數是該協議要用到的參數。home路由表項中type指向的是Zend\Mvc\Router\Http\Literal協議,options指向的是該協議用到的兩個參數route與defaults,在Literal協議中,route表示匹配的路由模式,而defaults中的內容表示相應的處理方法。以home路由表項爲例,當用戶的請求地址爲項目根地址時,就執行Application\Controller\Index控制器中的indexAction方法。這裏有一點要注意,controller指向的必須是一個在ControllerManager中註冊過的控制器,不然就返回ERROR_CONTROLLER_NOT_FOUND的錯誤。