一个自动渲染带布局的hello word为例。
在配置文件中设置布局文件的存放路径resources.layout.layoutPath = APPLICATION_PATH "/layouts/",引导程序会注册布局组件Zend_Layout。
在配置文件中设置布局文件的存放路径resources.layout.layoutPath = APPLICATION_PATH "/layouts/",引导程序会注册布局组件Zend_Layout。
//控制器
<?php
class IndexController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
}
}
//视图
<h2>hello word</h2>
//布局
<?php
echo $this->doctype();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<?php echo $this->headTitle();
echo $this->headLink()->appendStylesheet("css/layout.css");
?>
</head>
<body>
<div id="pageWrapper">
<div id="header">
<h1>This is layout</h1>
</div>
<div id="main">
<?php echo $this->layout()->content?>
</div>
<div id="footer">
<p><em>Powered by the qiangge</em></p>
</div>
</div>
</body>
</html>
显示结果为
下面分析一下是如何得到这个页面的:
1、在实例化Zend_Layout的时候,通过其构造方法在注册了一个Zend_Layout_Controller_Plugin_Layout插件和Zend_Layout_Controller_Action_Helper_Layout动作助手。
protected function _initMvc()
{
$this->_initPlugin();
$this->_initHelper();
}
2、在前端控制执行dispatch()的时候,在默认情况会通过动作助手经理人注册自动渲染插件 Zend_Controller_Action_Helper_ViewRenderer,
public function dispatch(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null)
{
if (!$this->getParam('noErrorHandler') && !$this->_plugins->hasPlugin('Zend_Controller_Plugin_ErrorHandler')) {
// Register with stack index of 100
require_once 'Zend/Controller/Plugin/ErrorHandler.php';
$this->_plugins->registerPlugin(new Zend_Controller_Plugin_ErrorHandler(), 100);
}
if (!$this->getParam('noViewRenderer') && !Zend_Controller_Action_HelperBroker::hasHelper('viewRenderer')) {
require_once 'Zend/Controller/Action/Helper/ViewRenderer.php';
Zend_Controller_Action_HelperBroker::getStack()->offsetSet(-80, new Zend_Controller_Action_Helper_ViewRenderer());
}
/**
* Instantiate default request object (HTTP version) if none provided
*/
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);
}
/**
* Set base URL of request object, if available
*/
if (is_callable(array($this->_request, 'setBaseUrl'))) {
if (null !== $this->_baseUrl) {
$this->_request->setBaseUrl($this->_baseUrl);
}
}
/**
* Instantiate default response object (HTTP version) if none provided
*/
if (null !== $response) {
$this->setResponse($response);
} elseif ((null === $this->_response) && (null === ($this->_response = $this->getResponse()))) {
require_once 'Zend/Controller/Response/Http.php';
$response = new Zend_Controller_Response_Http();
$this->setResponse($response);
}
/**
* Register request and response objects with plugin broker
*/
$this->_plugins
->setRequest($this->_request)
->setResponse($this->_response);
/**
* Initialize router
*/
$router = $this->getRouter();
$router->setParams($this->getParams());
/**
* Initialize dispatcher
*/
$dispatcher = $this->getDispatcher();
$dispatcher->setParams($this->getParams())
->setResponse($this->_response);
// Begin dispatch
try {
/**
* Route request to controller/action, if a router is provided
*/
/**
* Notify plugins of router startup
*/
$this->_plugins->routeStartup($this->_request);
try {
$router->route($this->_request);
} catch (Exception $e) {
if ($this->throwExceptions()) {
throw $e;
}
$this->_response->setException($e);
}
/**
* Notify plugins of router completion
*/
$this->_plugins->routeShutdown($this->_request);
/**
* Notify plugins of dispatch loop startup
*/
$this->_plugins->dispatchLoopStartup($this->_request);
/**
* Attempt to dispatch the controller/action. If the $this->_request
* indicates that it needs to be dispatched, move to the next
* action in the request.
*/
do {
$this->_request->setDispatched(true);
/**
* Notify plugins of dispatch startup
*/
$this->_plugins->preDispatch($this->_request);
/**
* Skip requested action if preDispatch() has reset it
*/
if (!$this->_request->isDispatched()) {
continue;
}
/**
* Dispatch request
*/
try {
$dispatcher->dispatch($this->_request, $this->_response);
} catch (Exception $e) {
if ($this->throwExceptions()) {
throw $e;
}
$this->_response->setException($e);
}
/**
* Notify plugins of dispatch completion
*/
$this->_plugins->postDispatch($this->_request);
} while (!$this->_request->isDispatched());
} catch (Exception $e) {
if ($this->throwExceptions()) {
throw $e;
}
$this->_response->setException($e);
}
/**
* Notify plugins of dispatch loop completion
*/
try {
$this->_plugins->dispatchLoopShutdown();
} catch (Exception $e) {
if ($this->throwExceptions()) {
throw $e;
}
$this->_response->setException($e);
}
if ($this->returnResponse()) {
return $this->_response;
}
$this->_response->sendResponse();
}
3、前端控制获取分发器进行分发,分发器定位到控制执行相应的动作,动作控制器Zend_Controller_Action::dispatch($action)。Zend_Controller_Action是控制器的基类,它控制请求返回对象,视图助手,视图渲染等。在每个动作执行前后会预留助手和该动作的钩子方法。
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);
}
// 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();
}
4、 执行完动作后,$this->_helper->notifyPostDispatch();调用视图经理人栈中的视图渲染助手进行视图渲染,将得到的结果存入响应对象中,默认放在属性_body['default']中。
public function postDispatch()
{
if ($this->_shouldRender()) {
$this->render();
}
}
...
public function renderScript($script, $name = null)
{
if (null === $name) {
$name = $this->getResponseSegment();
}
$this->getResponse()->appendBody(
$this->view->render($script),
$name
);
$this->setNoRender();
}
5、再回到Zend_Controller_Front::dispatch()中, $this->_plugins->postDispatch($this->_request)调用前端控制器插件栈中的Zend_Layout_Controller_Plugin_Layout插件,通过布局助手把第四步渲染好的视图内容以默认为content变量传到layout视图中。
/**
* postDispatch() plugin hook -- render layout
*
* @param Zend_Controller_Request_Abstract $request
* @return void
*/
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
$layout = $this->getLayout();
$helper = $this->getLayoutActionHelper();
// Return early if forward detected
if (!$request->isDispatched()
|| $this->getResponse()->isRedirect()
|| ($layout->getMvcSuccessfulActionOnly()
&& (!empty($helper) && !$helper->isActionControllerSuccessful())))
{
return;
}
// Return early if layout has been disabled
if (!$layout->isEnabled()) {
return;
}
$response = $this->getResponse();
$content = $response->getBody(true);
$contentKey = $layout->getContentKey();
if (isset($content['default'])) {
$content[$contentKey] = $content['default'];
}
if ('default' != $contentKey) {
unset($content['default']);
}
$layout->assign($content);
$fullContent = null;
$obStartLevel = ob_get_level();
try {
$fullContent = $layout->render();
$response->setBody($fullContent);
} catch (Exception $e) {
while (ob_get_level() > $obStartLevel) {
$fullContent .= ob_get_clean();
}
$request->setParam('layoutFullContent', $fullContent);
$request->setParam('layoutContent', $layout->content);
$response->setBody(null);
throw $e;
}
}
6、Zend_Controller_Front::dispatch()最后面,如果不是设置返回内容,响应对象直接输出内容。