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 视图资源插件,确保你的资源插件被使用。

你可能感兴趣的:(框架,function,application,Zend,interface,resources)