zendFramework分析3:控制器与视图

一个自动渲染带布局的hello word为例。
在配置文件中设置布局文件的存放路径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?>&nbsp;
</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()最后面,如果不是设置返回内容,响应对象直接输出内容。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章