7.3. 子类化(继承)

7.3.1.  简介

Zend_Controller体系具有可扩展性,可以通过继承已有的类或者自己写个新的类来实现Zend_Controller_Router_Interface或Zend_Controller_Dispatcher_Interface接口。

打算实现一个新的router或dispatcher的原因可能是:

  • 已有的URI routing系统不合适,如把用Zend Framework的站点集成到一个已经存在的有自己的定向规则的站点。

  • 你想用MVC模式来开发其它形式的PHP程序,如命令行形式的。那么你就需要自己定制一个router来处理命令行的参数了。

  • Zend_Controller_Dispatcher提供的机制不适用于你的程序。默认的情况是controller是类而action是类中的方法。但是也有其它情况如controller是一个目录,而action是其中的文件。这样你也要自己写。

  • 你想为你的控制器提供更多功能。例如Zend_Controller_Action默认没有和Zend_View集成。当然你也可以通过继承来实现,这样可以不用改变Zend_Controller_Router或者Zend_Controller_Dispatcher。

P当你覆写系统的重要部分时要非常小心,特别是dispatcher。Zend_Controller的优点之一是它对于构建各种应用程序规定了一些固定的规则。如果你改写太多,有可能就失去这个优点。不过,现实当中有各种不同的需要,不可能只采用一种解决方案,所以在需要的时候可以考虑自己来改写。

7.3.2. 约定(规则)

当继承任何Zend_Controller的类时,你最好遵循以下命名和文件存储规则。这样做可以让另一个熟悉Zend Framework的程序员很容易地理解你的项目。

7.3.2.1. 前缀

Zend Framework中的所有类的名称都有一个前缀“Zend_” ,我们建议你的类可以模仿这种方法,例如你的公司名称为Widget,则前缀可以是“Widget_” 。

7.3.2.2. 目录分布

Zend_Controller的各个类在library目录下是这样分布的:

/library
  /Zend
    /Controller
      Action.php
      Dispatcher.php
      Router.php

当继承Zend_Controller类时,建议新的类放在带有你的前缀的和以上结构完全相同的目录下。这样别人在查看你的代码时,可以很容易找到相应的代码。

例如,Widget公司的一个项目,只实现了一个自定义的router,看起来可能是这样的:

/library
  /Zend
  /Widget
    /Controller
      Router.php
      README.txt

注意在这个例子中Widget/Controller/映射到Zend/Controller/。另外,这个例子提供了一个Widget_Controller_Router,这可能是Zend_Controller_Router的子类,也可以是替代(通过实现Zend_Controller_Router_Interface接口)。

同时,注意上例中在Widget/Controller/下有个README.txt文件。我们强烈建议你为客户提供各种测试和文档,同时也建议你放置一个简单的README.txt文件在适当的目录,用来简要说明代码的修正过程和它们是如何工作的。

7.3.3. Router 接口

Zend_Controller_Router_Interface接口只定义了一个方法:

<?php
				
  /**				
   * @param  Zend_Controller_Dispatcher_Interface
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Dispatcher_Token|boolean
   */
  public function route(Zend_Controller_Dispatcher_Interface $dispatcher);

?>

Routing过程只发生一次:当请求被系统接收时。router的目的在于生成一个描述controller和action的Zend_Controller_Dispatch_Token。Token对象被传给dispatcher。如果无法将一个路径分配到一个dispatch token(无效的访问路径),则会返回false。

一些router需要处理动态的元素,需要判断生成的dispatch token在return之前是否是可分配的。 出于这个原因,router把对象当成它的route()方法的唯一的参数 。 dispatcher则提供了一个 isDispatchable()方法来测试是否是可分配的。

7.3.4. Dispatcher 接口

Zend_Controller_Front会先调用router,接收第一个dispatch token,然后传递给dispatcher。dispatcher将分配action(实例化一个controller,调用它的action)并返回一个FALSE或者另一个 dispatch token。

Zend_Controller_Front重复调用dispatcher直到不返回dispatch token为止。这就是所谓的dispatch循环,它让所有的action依次处理,直到处理完毕。

Zend_Controller_Dispatcher_Interface接口定义了两个方法:

<?php
				
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return boolean
 */
public function isDispatchable(Zend_Controller_Dispatcher_Token $route);

?>

isDispatchable()方法检查一个 dispatch token 是否是可分配的。如果可以,返回TRUE,否则返回FALSE。默认地对于Zend_Controller_Dispatcher,可分配的意思就是说controller文件存在,文件内也存在相应的类,并且类中存在相应的action方法,

<?php
			
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return Zend_Controller_Dispatcher_Token|boolean
 */
public function dispatch(Zend_Controller_Dispatcher_Token $route);

?>

dispatch()方法就是action执行的地方,必须执行controller的action。它可以返回一个dispatch token或者返回一个FALSE,表示工作已经全部完成。