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()最後面,如果不是設置返回內容,響應對象直接輸出內容。



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