Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5017731
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类:

2008-12-23 23:21:12

  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会因为没有初始化而出错。

 在实际开发中,本人觉得前端控制器的构造方法不应该是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中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);

 Zend Framework MVCplugin机制类似于ASP.NET中的HTTPModule,也就是说你添加的所有的plugin都会在处理具体的Action之前和之后触发相应的事件。一次请求只能有一个具体的action来处理(可以通过setDispatched的方法来让一个请求处理多个action,我们将这个做为一个小技巧 J ),但是可以有多个plugin。这个和ASP.NET中的HTTPHandlerHTTPModule完全类似。

 

 

一个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())

 如何去改变默认提供的这些路由、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();                                    
          ->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();

 

 Zend_Controller_Action_HelperBroker 类实现了Helper类到Action类的代理,是Zend 构架中一个很优秀的机制,这种模式有点类似注册工厂,也就是向HelperBroker注册对象,然后再从HelperBroker中取回对象,下面是注册工厂参考图

但是又不完全和注册工厂一样,在获取Helper类的时候是实例化了一个HelperBroker,又和MonoState模式做法一样,如果要和模式挂上钩的话,那么可以暂且称之为 “RegistryMonoState”

不足之处是PluginBroker类,并且在前端控制器的构造函数中实例化PluginBroker是非常不好的做法,PluginBroker也完全可以用HelperBroker类的做法,这样可以大大降低耦合性。

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仓库:

阅读(2085) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~