Zend Framework教程之动作的基类Zend_Controller_Action详解
本文实例讲述了zend framework教程之动作的基类zend_controller_action。分享给大家供大家参考,具体如下:
zend_controller_action的实现
zend framework的动作控制器需要继承zend_controller_action,zend_controller_action提供了动作控制器的基本功能,具体参考如下代码:
zend_controller_action_interface
<?php interface zend_controller_action_interface { /** * class constructor * * the request and response objects should be registered with the * controller, as should be any additional optional arguments; these will be * available via {@link getrequest()}, {@link getresponse()}, and * {@link getinvokeargs()}, respectively. * * when overriding the constructor, please consider this usage as a best * practice and ensure that each is registered appropriately; the easiest * way to do so is to simply call parent::__construct($request, $response, * $invokeargs). * * after the request, response, and invokeargs are set, the * {@link $_helper helper broker} is initialized. * * finally, {@link init()} is called as the final action of * instantiation, and may be safely overridden to perform initialization * tasks; as a general rule, override {@link init()} instead of the * constructor to customize an action controller's instantiation. * * @param zend_controller_request_abstract $request * @param zend_controller_response_abstract $response * @param array $invokeargs any additional invocation arguments * @return void */ public function __construct(zend_controller_request_abstract $request, zend_controller_response_abstract $response, array $invokeargs = array()); /** * dispatch the requested action * * @param string $action method name of action * @return void */ public function dispatch($action); }
zend_controller_action
<?php require_once 'zend/controller/action/helperbroker.php'; require_once 'zend/controller/action/interface.php'; require_once 'zend/controller/front.php'; abstract class zend_controller_action implements zend_controller_action_interface { protected $_classmethods; protected $_delimiters; protected $_invokeargs = array(); protected $_frontcontroller; protected $_request = null; protected $_response = null; public $viewsuffix = 'phtml'; public $view; protected $_helper = null; public function __construct(zend_controller_request_abstract $request, zend_controller_response_abstract $response, array $invokeargs = array()) { $this->setrequest($request) ->setresponse($response) ->_setinvokeargs($invokeargs); $this->_helper = new zend_controller_action_helperbroker($this); $this->init(); } public function init() { } public function initview() { if (!$this->getinvokearg('noviewrenderer') && $this->_helper->hashelper('viewrenderer')) { return $this->view; } require_once 'zend/view/interface.php'; if (isset($this->view) && ($this->view instanceof zend_view_interface)) { return $this->view; } $request = $this->getrequest(); $module = $request->getmodulename(); $dirs = $this->getfrontcontroller()->getcontrollerdirectory(); if (empty($module) || !isset($dirs[$module])) { $module = $this->getfrontcontroller()->getdispatcher()->getdefaultmodule(); } $basedir = dirname($dirs[$module]) . directory_separator . 'views'; if (!file_exists($basedir) || !is_dir($basedir)) { require_once 'zend/controller/exception.php'; throw new zend_controller_exception('missing base view directory ("' . $basedir . '")'); } require_once 'zend/view.php'; $this->view = new zend_view(array('basepath' => $basedir)); return $this->view; } public function render($action = null, $name = null, $nocontroller = false) { if (!$this->getinvokearg('noviewrenderer') && $this->_helper->hashelper('viewrenderer')) { return $this->_helper->viewrenderer->render($action, $name, $nocontroller); } $view = $this->initview(); $script = $this->getviewscript($action, $nocontroller); $this->getresponse()->appendbody( $view->render($script), $name ); } public function renderscript($script, $name = null) { if (!$this->getinvokearg('noviewrenderer') && $this->_helper->hashelper('viewrenderer')) { return $this->_helper->viewrenderer->renderscript($script, $name); } $view = $this->initview(); $this->getresponse()->appendbody( $view->render($script), $name ); } public function getviewscript($action = null, $nocontroller = null) { if (!$this->getinvokearg('noviewrenderer') && $this->_helper->hashelper('viewrenderer')) { $viewrenderer = $this->_helper->gethelper('viewrenderer'); if (null !== $nocontroller) { $viewrenderer->setnocontroller($nocontroller); } return $viewrenderer->getviewscript($action); } $request = $this->getrequest(); if (null === $action) { $action = $request->getactionname(); } elseif (!is_string($action)) { require_once 'zend/controller/exception.php'; throw new zend_controller_exception('invalid action specifier for view render'); } if (null === $this->_delimiters) { $dispatcher = zend_controller_front::getinstance()->getdispatcher(); $worddelimiters = $dispatcher->getworddelimiter(); $pathdelimiters = $dispatcher->getpathdelimiter(); $this->_delimiters = array_unique(array_merge($worddelimiters, (array) $pathdelimiters)); } $action = str_replace($this->_delimiters, '-', $action); $script = $action . '.' . $this->viewsuffix; if (!$nocontroller) { $controller = $request->getcontrollername(); $controller = str_replace($this->_delimiters, '-', $controller); $script = $controller . directory_separator . $script; } return $script; } public function getrequest() { return $this->_request; } public function setrequest(zend_controller_request_abstract $request) { $this->_request = $request; return $this; } public function getresponse() { return $this->_response; } public function setresponse(zend_controller_response_abstract $response) { $this->_response = $response; return $this; } protected function _setinvokeargs(array $args = array()) { $this->_invokeargs = $args; return $this; } public function getinvokeargs() { return $this->_invokeargs; } public function getinvokearg($key) { if (isset($this->_invokeargs[$key])) { return $this->_invokeargs[$key]; } return null; } public function gethelper($helpername) { return $this->_helper->{$helpername}; } public function gethelpercopy($helpername) { return clone $this->_helper->{$helpername}; } public function setfrontcontroller(zend_controller_front $front) { $this->_frontcontroller = $front; return $this; } public function getfrontcontroller() { // used cache version if found if (null !== $this->_frontcontroller) { return $this->_frontcontroller; } // grab singleton instance, if class has been loaded if (class_exists('zend_controller_front')) { $this->_frontcontroller = zend_controller_front::getinstance(); return $this->_frontcontroller; } // throw exception in all other cases require_once 'zend/controller/exception.php'; throw new zend_controller_exception('front controller class has not been loaded'); } public function predispatch() { } public function postdispatch() { } public function __call($methodname, $args) { require_once 'zend/controller/action/exception.php'; if ('action' == substr($methodname, -6)) { $action = substr($methodname, 0, strlen($methodname) - 6); throw new zend_controller_action_exception(sprintf('action "%s" does not exist and was not trapped in __call()', $action), 404); } throw new zend_controller_action_exception(sprintf('method "%s" does not exist and was not trapped in __call()', $methodname), 500); } public function dispatch($action) { // notify helpers of action predispatch state $this->_helper->notifypredispatch(); $this->predispatch(); if ($this->getrequest()->isdispatched()) { if (null === $this->_classmethods) { $this->_classmethods = get_class_methods($this); } // if pre-dispatch hooks introduced a redirect then stop dispatch // @see zf-7496 if (!($this->getresponse()->isredirect())) { // predispatch() didn't change the action, so we can continue if ($this->getinvokearg('usecasesensitiveactions') || in_array($action, $this->_classmethods)) { if ($this->getinvokearg('usecasesensitiveactions')) { trigger_error('using case sensitive actions without word separators is deprecated; please do not rely on this "feature"'); } $this->$action(); } else { $this->__call($action, array()); } } $this->postdispatch(); } // whats actually important here is that this action controller is // shutting down, regardless of dispatching; notify the helpers of this // state $this->_helper->notifypostdispatch(); } public function run(zend_controller_request_abstract $request = null, zend_controller_response_abstract $response = null) { if (null !== $request) { $this->setrequest($request); } else { $request = $this->getrequest(); } if (null !== $response) { $this->setresponse($response); } $action = $request->getactionname(); if (empty($action)) { $action = 'index'; } $action = $action . 'action'; $request->setdispatched(true); $this->dispatch($action); return $this->getresponse(); } protected function _getparam($paramname, $default = null) { $value = $this->getrequest()->getparam($paramname); if ((null === $value || '' === $value) && (null !== $default)) { $value = $default; } return $value; } protected function _setparam($paramname, $value) { $this->getrequest()->setparam($paramname, $value); return $this; } protected function _hasparam($paramname) { return null !== $this->getrequest()->getparam($paramname); } protected function _getallparams() { return $this->getrequest()->getparams(); } final protected function _forward($action, $controller = null, $module = null, array $params = null) { $request = $this->getrequest(); if (null !== $params) { $request->setparams($params); } if (null !== $controller) { $request->setcontrollername($controller); // module should only be reset if controller has been specified if (null !== $module) { $request->setmodulename($module); } } $request->setactionname($action) ->setdispatched(false); } protected function _redirect($url, array $options = array()) { $this->_helper->redirector->gotourl($url, $options); } }
zend_controller_action提供了动作和视图的render功能,以及注册请求和响应对象,常用助手等等。
动作控制器的常用方法
在动作控制器中常用的方法和属性如下:
$this->_helper主要完成助手的相关操作例如:
// 只是局部控制器;当初始化加载时,对这个控制器的所有动作有效: $this->_helper->viewrenderer->setnorender(true); // 全局: $this->_helper->removehelper('viewrenderer'); // 也是全局,但需要和本地版本协作,以便繁殖这个控制器: zend_controller_front::getinstance()->setparam('noviewrenderer', true);
通过设置viewrenderer的norender标记,可以简单地为一个独立的视图禁止解析(rendering):
class foocontroller extends zend_controller_action { public function baraction() { // disable autorendering for this action only: $this->_helper->viewrenderer->setnorender(); } }
禁止viewrenderer的主要原因是如果你不需要视图对象或者如果你不通过视图脚本(例如,当使用动作控制器来司服网站服务协议如soap,xml-rpc或rest)来解析。大多数情况下,你不需要全局地禁止viewrenderer,只选择性地在个别控制器或动作里禁止它。
请求对象和响应对象的相关操作
无数的对象和变量与对象一起注册,并且每个都有访问器方法。
请求对象:getrequest()可用来读取调用动作请求对象。
响应对象: getresponse()可用来读取收集最终响应的响应对象。一些典型的调用看起来象这样:
$this->getresponse()->setheader('content-type', 'text/xml'); $this->getresponse()->appendbody($content);
调用参数:前端控制器可能把参数传给路由器、派遣器和动作控制器。为了读取这些参数,可使用getinvokearg($key);另外,用getinvokeargs()读取整个参数列表。
请求参数:请求对象手机请求参数,如任何_get 或 _post 参数,或者指定在url的路径信息里的用户参数。为了读取这些参数,可使用_getparam($key)或_getallparams()。也可以用_setparam()来设置请求参数;当转发到另外的动作时这很有用。
用_hasparam($key)来测试是否一个参数存在(对逻辑分支有用)。
note: _getparam()可带有一个可选的第二个参数,如果它不是空的,就包含一个缺省的值。用它在读取值之前来消除对_hasparam() 的调用:
// use default value of 1 if id is not set $id = $this->_getparam('id', 1); // instead of: if ($this->_hasparam('id') { $id = $this->_getparam('id'); } else { $id = 1; }
视图的相关操作
zend_controller_action为视图继承提供了一个初步的灵活的机制。有两个方法来完成这个:initview() 和 render();前者松散地加载$view public 属性,后者基于当前请求的动作来解析视图,它们使用目录层次来决定脚本路径。
视图初始化
initview()初始化视图对象。为了读取视图对象,render()调用initview(),但它可以在任何时候被初始化;缺省地,它用zend_view对象来组装$view属性,但任何实现zend_view_interface的类可以使用。如果$view已经被初始化,它就简单地返回属性。
缺省的实现使用下面假设的目录结构:
applicationormodule/
controllers/
indexcontroller.php
views/
scripts/
index/
index.phtml
helpers/
filters/
换句话说,视图脚本假定放在views/scripts/子目录,同时假定 views子目录还包含兄弟功能(助手和过滤器)。确定视图脚本名称和路径时,先以 views/scripts/作为基路径,然后加上以视图脚本对应控制器命名的目录。
解析(rendering)视图
render() 有下列特征:has the following signature:
string render(string $action = null, string $name = null, bool $nocontroller = false);
render()解析视图脚本。如果没有传递参数,它假定请求的脚本是[controller]/[action].phtml (.phtml是$viewsuffix属性的值)。为$action传递一个值将解析在[controller]子目录中的模板。为用[controller]重写,传递一个true值给$nocontroller。最后,模板被解析到响应对象;如果你希望解析到一个在响应对象里指定的named segment,传递一个值给$name。
note: 因为控制器和动作名字里可能包含分隔符如'_'、 '.' 和 '-',当决定视图名字时,render()把它们规格化成 '-'.在内部,它使用派遣器的字和路径分隔符来做规格化。这样,对/foo.bar/baz-bat的请求将解析脚本foo-bar/baz-bat.phtml。如果动作方法包含camelcasing,记住当决定视图脚本文件名的时候,这将变成由'-'分隔的字。
一些例子:
class mycontroller extends zend_controller_action { public function fooaction() { // renders my/foo.phtml $this->render(); // renders my/bar.phtml $this->render('bar'); // renders baz.phtml $this->render('baz', null, true); // renders my/login.phtml to the 'form' segment of the // response object $this->render('login', 'form'); // renders site.phtml to the 'page' segment of the response // object; does not use the 'my/' subirectory $this->render('site', 'page', true); } public function bazbataction() { // renders my/baz-bat.phtml $this->render(); } }
其它
_forward($action, $controller = null, $module = null, array $params = null) :执行另外一个动作。如果在predispatch()里调用,当前请求的动作将被跳过来支持新的动作。否则,在当前动作被处理之后,在_forward()请求的动作将被执行。
_redirect($url, array $options = array()):重定向到另外一个地方。这个方法用url和一组可选的选项。缺省地,它执行http 302 重定向。
选项可包括一个或多个下面这些:
exit:是否立即退出。如果被请求,它将干净地关闭任何打开的会话和执行重定向。
可以用setredirectexit()访问器在控制器里全局地设置这个选项。
prependbase:是否预先考虑基础url和url提供的请求对象一起注册。
使用setredirectprependbase()访问器,在控制器里全局地设置这个选项。
code:在重定向时要用什么http代码。缺省使用302;可以用从301到306之间的任何代码。
使用setredirectcode()访问器,在控制器里全局地设置这个选项。
扩展自定义zend_controller_action
为了创建动作控制器,设计上,zend_controller_action 必须被继承。至少,需要定义控制器可能调用的动作方法。
除了为web应用程序创建有用的函数外,你可能发现在不同的控制器里重复同样的设置和实用方法;如果这样,创建一个继承(extends)zend_controller_action 的基础类可能会解决问题。
example #1 如何处理不存在的动作
如果控制器的请求包括一个未定义的动作方法,zend_controller_action::__call()将被调用。__call()当然是php中用来重载方法的魔术方法。
缺省地,这个方法抛出一个zend_controller_action_exception 来表明在控制器里没有发现要求的方法。如果要求的方法以'action'结尾,就假设一个动作被请求并且不存在;这样的错误导致带有代码为 404 的异常。所有其它方法导致带有代码为 500 的异常。这使你很容易地在错误句柄里区分是页面没有发现还是程序错误。
如果想执行其它操作,你应该重写这个函数。例如,如果你想显示错误信息,可以象下面这样来写:
class mycontroller extends zend_controller_action { public function __call($method, $args) { if ('action' == substr($method, -6)) { // if the action method was not found, render the error // template return $this->render('error'); } // all other methods throw an exception throw new exception('invalid method "' . $method . '" called', 500); } }
另外的可能性就是你可能想转发到缺省控制页面:
class mycontroller extends zend_controller_action { public function indexaction() { $this->render(); } public function __call($method, $args) { if ('action' == substr($method, -6)) { // if the action method was not found, forward to the // index action return $this->_forward('index'); } // all other methods throw an exception throw new exception('invalid method "' . $method . '" called', 500); } }
为了定制控制器,除了重写__call()以外,本章前面说涉及的初始化、实用程序、访问器、视图和派遣钩子等方法都可以被重写。作为例子,如果把视图对象保存到注册表里,你可能想用象下面的代码来修改initview():
abstract class my_base_controller extends zend_controller_action { public function initview() { if (null === $this->view) { if (zend_registry::isregistered('view')) { $this->view = zend_registry::get('view'); } else { $this->view = new zend_view(); $this->view->setbasepath(dirname(__file__) . '/../views'); } } return $this->view; } }
更多关于zend相关内容感兴趣的读者可查看本站专题:《zend framework框架入门教程》、《php优秀开发框架总结》、《yii框架入门及常用技巧总结》、《thinkphp入门教程》、《php面向对象程序设计入门教程》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家php程序设计有所帮助。
推荐阅读
-
Zend Framework教程之动作的基类Zend_Controller_Action详解
-
Zend Framework教程之动作的基类Zend_Controller_Action详解,controller基类_PHP教程
-
Zend Framework教程之动作的基类Zend_Controller_Action详解
-
Zend Framework教程之动作的基类Zend_Controller_Action详解_php实例
-
Zend Framework教程之动作的基类Zend_Controller_Action详解,controller基类_PHP教程
-
Zend Framework教程之动作的基类Zend_Controller_Action详解
-
Zend Framework教程之动作的基类Zend_Controller_Action详解_PHP
-
Zend Framework教程之动作的基类Zend_Controller_Action详解_PHP
-
Zend Framework教程之动作的基类Zend_Controller_Action详解
-
Zend Framework教程之动作的基类Zend_Controller_Action详解,controller基类