Zend Framework 剖析之MVC

Zend Framework 剖析之MVC

【开篇】

  Web开发中,除了ASP.NETPage Controller之外,MVC是其他开发语言中一个非常重要和常用的架构模式,本文就Zend Framework中的 MVC处理流程做一下浅显的分析。

 

【结构】

这里是一个Zend Framework 开发项目的目录结构,可以做为参考。具体的Front Controller 设计模式、MVC等可以参阅相关资料。目录结构如下:


 
/app/controllers       所有的controller
/app/models              所有的model
/app/views/script view的模板文件夹
/config                        存放config文件
/db                               数据库相关脚步、sql存放
/lib                               库文件,如/lib/Zend存放Zend Framework/lib/YourProj可以存放自己项目的库文件
/log                             日志文件
/root                            根目录,该文件夹下只有一个文件index.php即前端控制器,.htaccessapache配置文件,将所有的请求转发到index.php
 

一个典型的前端控制器代码:

 $ctrl = Zend_Controller_Front::getInstance();

 $ctrl ->throwExceptions(true);

 $ctrl ->dispatch();

 OK,一个dispatch就处理完了所有的请求,将处理结果传给了客户端,那么我们就从 Zend_Front_Controller来开始我们的Zend Framework之旅吧。

 

【流程分析】

首先,打开文件 /lib/Zend/Controller/Front.php文件。

 

getInstance 方法:

   很简单的  Singleton 设计模式。下面进入构造函数。

 

__construct 方法:

    $this->_plugins = new Zend_Controller_Plugin_Broker();

初始化当前 _plugins 字段。

在 实际开发中,本人觉得前端控制器的构造方法不应该是private,而应改成protected,理由很简单,如果要继承该Front Controller,那么就可以在构造函数中调用父类的构造函数,private的则没有办法实现,如果你要继承Front Controller的话,那必须在你的构造函数中加上一句: $this->_plugins = new Zend_Controller_Plugin_Broker();否则,当前的_plugins会因为没有初始化而出错。

Text Box:   在实际开发中,本人觉得前端控制器的构造方法不应该是private,而应改成protected,理由很简单,如果要继承该Front Controller,那么就可以在构造函数中调用父类的构造函数,private的则没有办法实现,如果你要继承Front Controller的话,那必须在你的构造函数中加上一句:
$this->_plugins = new Zend_Controller_Plugin_Broker();否则,当前的_plugins会因为没有初始化而出错。

构造完之后,我们来重点分析一下dispatch方法。

 

dispatch方法:

  dispatch方法是整个Zend MVC处理过程的核心,由于代码比较多,这里采用注释加评说的方法来讲解。

dispatch主要包括以下几个阶段:

(1)初始化阶段

/**

* 判断当前参数中是否设定了noErrorHandler参数,如果有或者已经有

* Plugin_ErrorHandler就不加载 这个plugin,否则就加载这个Plugin

*/

if (!$this->getParam('noErrorHandler') && !$this->_plugins->hasPlugin('Zend_Controller_Plugin_ErrorHandler')) {

        // Register with stack index of 100

$this->_plugins->registerPlugin(new     Zend_Controller_Plugin_ErrorHandler(), 100

);

}

/**

*判断当前参数是否设定了noViewRenderer参数,如果有或者已经有了一个viewRenderer

*就不添加 Helper_ViewRenderer,否则就添加

*Zend_Controller_Action_Helper_ViewRenderer 实例作为默认ViewRenderer

*/

if (!$this->getParam('noViewRenderer')  && !Zend_Controller_Action_HelperBroker::hasHelper('viewRenderer')) {

Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_ViewRenderer()

);

}

Zend Framework 剖析之MVC_第1张图片
Zend Framework MVC中plugin机制类似于ASP.NET中的HTTPModule,也就是说你添加的所有的plugin都会在处理具体的Action之前和之后触 发相应的事件。一次请求只能有一个具体的action来处理(可以通过setDispatched的方法来让一个请求处理多个action),但是可以有多个plugin。这个和ASP.NET中的HTTPHandler和HTTPModule完全类似。 一个plugin必须继承Zend_Controller_Plugin_Abstract 类,重载某些方法来实现相应的功能。 主要的事件有:

public function preDispatch(Zend_Controller_Request_Abstract $request){} public function postDispatch(Zend_Controller_Request_Abstract $request){}

preDispatch是在调用具体action之前触发,所以在这个地方,我们可以添加权限认证,URL转发等动作。
postDispatch是在调用action之后触发,在这个地方,可以做输出缓存等。 如何注册一个plugin呢?前端控制器中有一个方法registerPlugin,所以最简单的办法就是在index.php中初始化一个 plugin,然后调用 $ctrl->registerPlugin(myPlugin);

Text Box:   Zend Framework MVC中plugin机制类似于ASP.NET中的HTTPModule,也就是说你添加的所有的plugin都会在处理具体的Action之前和之后触发相应的事件。一次请求只能有一个具体的action来处理(可以通过setDispatched的方法来让一个请求处理多个action,我们将这个做为一个小技巧 J ),但是可以有多个plugin。这个和ASP.NET中的HTTPHandler和HTTPModule完全类似。
一个plugin必须继承Zend_Controller_Plugin_Abstract 类,重载某些方法来实现相应的功能。主要的事件有:
public function preDispatch(Zend_Controller_Request_Abstract $request){}
public function postDispatch(Zend_Controller_Request_Abstract $request){}
preDispatch是在调用具体action之前触发,所以在这个地方,我们可以添加权限认证,URL转发等动作。
postDispatch是在调用action之后触发,在这个地方,可以做输出缓存等。
如何注册一个plugin呢?前端控制器中有一个方法registerPlugin,所以最简单的办法就是在index.php中初始化一个plugin,然后调用 $ctrl->registerPlugin(myPlugin);

/**

  * 如果没有提供Request对象,则用默认的Request对象

  */

if (null !== $request) {

            $this->setRequest($request);

} elseif ((null === $request)

        && (null === ($request = $this->getRequest()))) {

            require_once 'Zend/Controller/Request/Http.php';

            $request = new Zend_Controller_Request_Http();

            $this->setRequest($request);

}

Response 对象的处理类似,在此略过。

/**

 * 给所有注册的plugin添加requestrepsonse对象

 */

 $this->_plugins

       ->setRequest($this->_request)

       ->setResponse($this->_response);

/**

 *初始化一个Router,默认的路由是用 Zend_Controller_Router_Rewrite

*/

$router = $this->getRouter();

$router->setParams($this->getParams());

 /**

  *初始化一个dispatcher,默认的dispatcher

  *Zend_Controller_Dispatcher_Standard

  */

$dispatcher = $this->getDispatcher();

$dispatcher->setParams($this->getParams())

          ->setResponse($this->_response);

  如何去改变默认提供的这些路由、dispatcher以及其他参数对象呢?在Zend_Controller_Front中都有类似 setXYZ的方法,以供设置这些对象,当然也有 对应的getXYZ来获取这些对象。所以一个典型的前端控制器的初始化也通常有如下写法:

$front    -> setRouter( $router
-> setDispatcher( new  Zend_Controller_ModuleDispatcher())
-> registerPlugin( new  My_Plugin_Auth( $auth ,   $acl )) 
-> registerPlugin( new  My_Plugin_View( $view )) 
-> setControllerDirectory(
    array ' default '   =>   realpath ( ' ../app/controllers/default ' ) ,
                ' admin '   =>   realpath ( ' ../app/controllers/admin ' )
   ))
-> setParam( ' auth ' ,   $auth
-> setParam( ' view ' ,   $view
-> setParam( ' config ' ,   $config
-> setParam( ' sitemap ' ,   $sitemap
-> dispatch();


2)路由阶段

/**

* 触发所有pluginrouteStartup事件

*/

$this->_plugins->routeStartup($this->_request);

/**

 * 路由当前请求,给request对象添加一些重要参数:modulecontrolleraction

 * 表示当前的请求由哪一个module 那一个controller的那个action处理

 */

$router->route($this->_request);

/**

* 触发所有pluginrouteShutdown事件

*/

$this->_plugins->routeShutdown($this->_request);

/**

 * 触发所有plugindispatchLoopStartup事件

 */

$this->_plugins->dispatchLoopStartup($this->_request);

 

(3)分发处理

/**

* 设置当前已经被dispatch

*/

 $this->_request->setDispatched(true);

/**

* 触发所有pluginpreDispatch

 */

 $this->_plugins->preDispatch($this->_request);

 /**

* 最重要的方法,该方法处理了以下动作:从request中找到当前的modulecontroller

   * action,然后实例化相应的类,最后执行action方法

   */

 $dispatcher->dispatch($this->_request, $this->_response);

 /**

  * 触发所有plugin  postDispatch

  */

 $this->_plugins->postDispatch($this->_request);       

/**

  * 触发所有plugin  dispatchLoopShutdown

  */

 $this->_plugins->dispatchLoopShutdown();   

 

(4)收尾阶段

如果要返回response对象,那么就返回当前的Response,否则就将response对象中的内容直接输出。

可以在这里返回response对象,然后对response对象做一些自定义的处理。

if ($this->returnResponse()) {

    return $this->_response;

}

$this->_response->sendResponse();

 

Text Box:   Zend_Controller_Action_HelperBroker 类实现了Helper类到Action类的代理,是Zend 构架中一个很优秀的机制,这种模式有点类似注册工厂,也就是向HelperBroker注册对象,然后再从HelperBroker中取回对象,下面是注册工厂参考图
但是又不完全和注册工厂一样,在获取Helper类的时候是实例化了一个HelperBroker,又和MonoState模式做法一样,如果要和模式挂上钩的话,那么可以暂且称之为 “RegistryMonoState” 。
不足之处是PluginBroker类,并且在前端控制器的构造函数中实例化PluginBroker是非常不好的做法,PluginBroker也完全可以用HelperBroker类的做法,这样可以大大降低耦合性。

 

【参考】
Zend Framework 开发Jira地址: http://framework.zend.com/issues/secure/Dashboard.jspa

      SVN仓库:http://framework.zend.com/svn/framework/trunk

 

 

你可能感兴趣的:(mvc,asp.net,action,Zend,plugins,HttpModule)