天天看點

Zend 架構之 Zend_Application 之(二):運作原理

當一個 MVC 應用程式被要求加入更多特性的時候,例如:建立資料庫,配置你的視圖和視圖助手,配置你的模闆,注冊插件,注冊動作助手等等,要配置好這個 MVC 應用程式并做好分發的準備,将會增加大量的代碼。

另外,你會經常希望重用相同代碼來引導你的測試、一個 cronjob(定時工作)、或者一個服務腳本。當隻需要簡單包括你的引導腳本成為可能的情況下,多數時候這個引導腳本是關于運作環境的初始化設定 - 你不必為了一個 cronjob 建立 MVC,或者為了一個服務腳本隻需要提供 DB 層。

Zend_Application 以簡化這個過程和提高重用性為目标,方法是通過把引導過程封裝進 OOP 規範。

Zend_Application 被分成3個部分:

  • Zend_Application:加載 PHP 環境,包括 include_path 路徑和自動加載,以及執行個體化被請求的引導類。
  • Zend_Application_Bootstrap:為引導類提供接口。Zend_Application_Bootstrap_Bootstrap 為絕大多數的引導需求提供常用功能,包括依賴性檢查算法和加載被請求的引導資源。
  • Zend_Application_Resource 為标準引導資源提供了一個接口,這些标準引導資源可以被一個引導執行個體按需加載;同時 Zend_Application_Resource 還提供了一些預設的資源接口。

開發者通過繼承 Zend_Application_Bootstrap_Bootstrap 類或者應用(最低程度的)Zend_Applicatioon_Bootstrap_Bootstrapper 接口,進而為他們的應用程式建立引導類。接入點(例如,public/index.php)将加載 Zend_Application,并傳遞以下參數來執行個體化它:

  • 目前的環境
  • 引導選項

引導選項包括了到包含引導類檔案的路徑和以下可選的内容:

  • 任何需要設定的額外 include_path 路徑
  • 任何需要注冊的額外自動加載器命名空間
  • 任何需要初始化的 php.ini 設定
  • 引導類的類名(如果類名不是“Bootstrap”)
  • 将被使用的資源的字首和路徑(成對出現)
  • 任何将被使用的資源(通過類名或者短名)
  • 需要另外加載配置檔案的路徑
  • 額外的配置選項

選項可以是一個數組,一個 Zend_Config 對象,或者到一個配置檔案的路徑。

引導程序

Zend_Application 職責的第二個領域是執行應用程式的引導程式。引導程式最低程度需要應用 Zend_Application_Bootstrap_Bootstrapper 接口,這個類定義了如下 API:

interface Zend_Application_Bootstrap_Bootstrapper

{

public function __construct($application);

public function setOptions(array $oprions);

public function getApplication();

public function getEnvironment();

public function getClassResources();

public function getClassResourceNames();

public function bootstrap($resource = null);

public function run();

}

這個 API 允許引導程式接受來自應用程式對象(application object)的環境和設定,把資源對于引導過程的職責報告給該資源,然後引導和運作應用程式。

你可以根據你的要求應用這個接口,繼承 Zend_Application_Bootstrap_BootstrapAbstract 或者 Zend_Application_Bootstrap_Bootstrap。

除了這些功能,你還應該熟悉其它大量的相關内容。

資源方法

Zend_Application_Bootstrap_BootstrapAbstract 接口為定義資源方法提供了一個簡單約定。任何以一個 _init 字首名開始的受保護方法将會被視作一個資源方法。

為引導一個單獨的資源方法,使用 bootstrap() 方法,然後把資源的名字傳遞給它。資源的名字将會是資源方法名稱去掉 _init 字首。

為了引導幾個資源方法,傳遞資源名稱數組給它。若是想引導全部資源方法,什麼參數都不要傳遞。

看看下面的這個引導類:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap

{

protected function _initFoo()

{

// ...

}

protected function _initBar()

{

// ...

}

protected function _initBaz()

{

// ...

}

}

隻想引導 _initFoo() 方法的話,這樣做:

$bootstrap->bootstrap('foo');

想引導 _initFoo() 和 _initBar() 方法,這樣做:

$bootstrap->bootstrap(array('foo', 'bar'));

想引導全部的資源方法,不帶任何參數調用 bootstrap():

$bootstrap->bootstrap();

使用資源插件的引導

為了使你的引導程式更加可重用,我們提供了以下功能:把你的資源加入到資源插件類中。這将允許你隻需簡單的通過配置就可混用和比對資源。我們會在稍後提到如何建立資源;在這個部分,我們隻會展示如何使用他們。

如果你希望引導程式能夠使用資源插件,你将需要應用一個額外的接口,Zend_Application_Bootstrap_ResourceBootstrapper。這個接口定義了一個如下的 API:定位,注冊和加載資源插件:

interface Zend_Application_Bootstrap_ResourceBootstrapper

{

public function registerPluginResource($reource, $options = null);

public function unregisterPluginResource($resource);

public function hasPluginResource($resource);

public function getPluginResource($resource);

public function getPluginResources();

public function getPluginResourceNames();

public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader);

public function getPluginLoader();

}

資源插件基本上提供了建立資源初始器的功能,這些資源初始器可以在應用程式間重複使用。這将允許你保持啟動程式的相對幹淨,同時不必修改引導程式也可以引入新的資源。

Zend_Application_Bootstrap_BootstrapAbstract(同時它的子類 Zend_Application_Bootstrap_Bootstrap)應用了這個接口,它将允許你使用資源插件。

為了使用資源插件,你應該在傳遞給應用程式對象以及/或引導程式)的選項中指定它們(資源插件)。這些選項可能來自一個配置檔案,或者手動傳遞。選項将會是成對出現的鍵名和選項值,其中鍵名表示資源的名字。資源的名字将會是類字首後面的所有東西。例如,Zend Framework 内置的資源有 Zend_Application_Resource_ 這個字首,任何這個字首後面的東西将會是這個資源的名字。請看:

$application = new Zend_Application(APPLICATION_ENV, array(

'resources' => array(

'FrontController' => array(

'controllerDirectory' => APPLICATION_PATH . '/controllers',

),

),

));

這将提示:FrontController 資源将會根據指定的選項被使用。

如果你開始寫自己的資源插件,或者利用第三方資源插件,你将需要告訴你的引導程式到哪裡找到它們。在内部,引導程式使用 Zend_Loader_PluginLoader,是以你将隻需提示它:通用類字首和路徑。

例子,讓我們假設你在 APPLICATION_PATH/resources/ 目錄下有定制的資源插件,它們都有 My_Resource 這個通用類字首。你可能把以下的資訊傳遞給應用程式對象:

$application = new Zend_Application(APPLICATION_ENV, array(

'pluginPaths' => array(

'My_Resource' => APPLICATION_PATH . '/resources/',

),

'resources' => array(

'FrontController' => array(

'controllerDirectory' => APPLICATION_PATH . '/controllers',

),

),

));

你現在應該可以使用那個目錄下的資源了。

正如前面提到的資源方法,你使用 bootstrap() 方法來執行資源插件。和資源方法一樣,你可以指定一個單獨的資源插件,多個插件(通過一個數組),或者全部插件。另外,你也可以混用和比對執行資源方法和資源插件。

// 執行一個插件:

$bootstrap->bootstrap('FrontController');

// 執行 N 個插件:

$bootstrap->bootstrap(array('FrontController', 'Foo'));

// 執行全部的資源插件和方法:

$bootstrap->bootstrap();

資源注冊

許多,如果不是全部,你的資源方法或者插件将初始化對象,在大多數情況下,這些對象将會被要求出現在應用程式的其它地方。你如何通路他們(對象)?

Zend_Application_Bootstrap_BootstrapAbstract 為這些對象提供了一個本地系統資料庫。為了在這些系統資料庫上儲存你的對象,你隻需簡單的從你的資源傳回它們就行了。

為了最大的靈活性,這個系統資料庫在内部被當作一個“容器”引用;它唯一的要求就是它是一個對象。資源然後被當作屬性(這個屬性以資源的名字命名)注冊。預設的,一個 Zend_Registry 的執行個體被使用,但是你也可以指定使用任何其它對象。setContainer() 和 getContainer() 方法可以被用來對容器進行操作。getResource($resource) 可以用來捕獲容器内的一個指定的資源,hasResource($resource) 用來檢查資源是否已經真正被注冊。

舉例說明,看下面的視圖資源:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap

{

protected function _initView()

{

$view = new Zend_View();

// more initialiazation...

return $view;

}

}

你可以通過以下代碼檢查和/或捕獲它:

// 使用 has/getResource() 方法:

if ($bootstrap->hadResource('view')) {

$view = $bootstrap->getResource('view');

}

// 通過容器:

$container = $bootstrap->getContainer();

if (isset($container->view)) {

$view = $container->view;

}

請注意,系統資料庫和容器都不是全局性的。這意味着為了捕獲資源你需要通路引導對象(bootstrap)。Zend_Application_Bootstrap_Bootstrap 為這提供了一些便利:在它的 run() 運作過程中,它把自己當作前端控制器的參數“bootstrap”注冊了,這将允許你從路由、分發器、插件和動作控制器捕獲它。

舉例說明,上述代碼中,如果你想從動作控制器内通路視圖資源,你可以這樣做:

class FooController extends Zend_Controller_Action

{

public function init()

{

$bootstrap = $this->getInvokeArg('bootstrap');

$view = $bootstrap->getResource('view');

// ...

}

}

依賴性檢查

為了執行資源方法和插件,確定它們被執行一次并且隻有一次,是十分必要的;這些意味着為了引導一個應用程式,執行多次,将會導緻資源重載。

同時,一些資源可能依賴于其它資源的執行。為了解決這兩個問題,Zend_Application_Bootstrap_BootstrapAbstract 提供了一個簡單,有效的依賴性檢查機制。

在前面已經說明,全部的資源 —— 無論是方法還是插件 —— 是通過調用引導對象bootstrap($resource) 來啟動的,$resource 是資源的名字,一個資源數組,或者,留白訓示運作全部的資源。

如果一個資源依賴于其它的資源,它應該在它的内部代碼調用 bootstrap() 來確定那個資源(依賴的資源)已經運作。後面對它(bootstrap()?)的調用将被忽略。

在一個資源方法内,這樣的調用看起來如下:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap

{

protected function _initRequest()

{

// 確定前端控制器已經初始化

$this->bootstrap('FrontController');

// 從引導系統資料庫中檢索前端控制器

$front = $this->getResource('FrontController');

$request = new Zend_Controller_Request_Http();

$request->setBaseUrl('/foo');

$front->setRequest($request);

// 確定 request 資源已經儲存到引導系統資料庫中

return $request;

}

}

資源插件

前面已經提到,建立可重用的引導資源,以及把代碼分散到分離的類當中,好的辦法是使用資源插件。雖然 Zend Framework 内置了大量的标準資源插件,但是開發者仍有必要自己寫的資源插件來封裝他們的初始化需求。

資源插件隻需要應用 Zend_Application_Resource_Resource 接口,或者,更簡單的,繼承 Zend_Application_Resource_ResourceAbstract。基本的接口很簡單:

interface Zend_Application_Resource_Resource

{

public function __construct($options = null);

public function setBootstrap(

Zend_Application_Bootstrap_Bootstrap $bootstrap

);

public function getBootstrap();

public function setOptions(array $options);

public function getOptions();

public function init();

}

這個接口簡單的定義了,一個資源插件構造函數應該接受的選項,有設定和檢索選項的功能,有設定和檢索引導對象的功能,以及一個初始化的方法。

舉例說明,讓我們假設在你的應用程式内有一個通用的初始化視圖,有一個通用的 doctype,CSS,和 JavaScript,同時你希望可以通過設定來傳遞一個基本的文檔标題。這樣的資源插件看起來是這樣的:

class My_Resource_View extends Zend_Application_Resource_ResourceAbstract

{

protected $_view;

public function init()

{

// 傳回 view 以便 bootstrap 會把它儲存到系統資料庫中

return $this->getView();

}

public function getView()

{

if (null === $this->_view) {

$options = $this->getOptions();

$title = '';

if (array_key_exists('title', $options)) {

$title = $options['title'];

unset($options['title']);

}

$view = new Zend_View($options);

$view->doctype('XHTML1_STRICT');

$view->headTitle($title);

$view->headLink()->appendStylesheet('/css/site.css');

$view->headScript()->appendfile('/js/analytics.js');

$viewRenderer =

Zend_Controller_Action_HelperBroker::getStaticHelper(

'ViewRenderer'

);

$viewRenderer->setView($view);

$this->_view = $view;

}

return $this->_view;

}

}

一旦你為這個資源插件注冊了字首路徑,你就可以把它用在你的應用程式中了。更妙的是,因為它使用了插件加載器,你将有效的重寫 Zend Framework 内置的 View 視圖資源插件,確定你的資源插件被使用。

繼續閱讀