Home  >  Article  >  php教程  >  Detailed explanation of Zend Framework tutorial action base class Zend_Controller_Action

Detailed explanation of Zend Framework tutorial action base class Zend_Controller_Action

高洛峰
高洛峰Original
2017-01-03 13:16:461248browse

The example in this article describes the base class Zend_Controller_Action of the action in the Zend Framework tutorial. Share it with everyone for your reference, as follows:

Implementation of Zend_Controller_Action

The action controller of Zend Framework needs to inherit Zend_Controller_Action. Zend_Controller_Action provides the basic functions of the action controller. Please refer to the following code for details:

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&#39;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 &#39;Zend/Controller/Action/HelperBroker.php&#39;;
require_once &#39;Zend/Controller/Action/Interface.php&#39;;
require_once &#39;Zend/Controller/Front.php&#39;;
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 = &#39;phtml&#39;;
  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(&#39;noViewRenderer&#39;) && $this->_helper->hasHelper(&#39;viewRenderer&#39;)) {
      return $this->view;
    }
    require_once &#39;Zend/View/Interface.php&#39;;
    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 . &#39;views&#39;;
    if (!file_exists($baseDir) || !is_dir($baseDir)) {
      require_once &#39;Zend/Controller/Exception.php&#39;;
      throw new Zend_Controller_Exception(&#39;Missing base view directory ("&#39; . $baseDir . &#39;")&#39;);
    }
    require_once &#39;Zend/View.php&#39;;
    $this->view = new Zend_View(array(&#39;basePath&#39; => $baseDir));
    return $this->view;
  }
  public function render($action = null, $name = null, $noController = false)
  {
    if (!$this->getInvokeArg(&#39;noViewRenderer&#39;) && $this->_helper->hasHelper(&#39;viewRenderer&#39;)) {
      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(&#39;noViewRenderer&#39;) && $this->_helper->hasHelper(&#39;viewRenderer&#39;)) {
      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(&#39;noViewRenderer&#39;) && $this->_helper->hasHelper(&#39;viewRenderer&#39;)) {
      $viewRenderer = $this->_helper->getHelper(&#39;viewRenderer&#39;);
      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 &#39;Zend/Controller/Exception.php&#39;;
      throw new Zend_Controller_Exception(&#39;Invalid action specifier for view render&#39;);
    }
    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, &#39;-&#39;, $action);
    $script = $action . &#39;.&#39; . $this->viewSuffix;
    if (!$noController) {
      $controller = $request->getControllerName();
      $controller = str_replace($this->_delimiters, &#39;-&#39;, $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(&#39;Zend_Controller_Front&#39;)) {
      $this->_frontController = Zend_Controller_Front::getInstance();
      return $this->_frontController;
    }
    // Throw exception in all other cases
    require_once &#39;Zend/Controller/Exception.php&#39;;
    throw new Zend_Controller_Exception(&#39;Front controller class has not been loaded&#39;);
  }
  public function preDispatch()
  {
  }
  public function postDispatch()
  {
  }
  public function __call($methodName, $args)
  {
    require_once &#39;Zend/Controller/Action/Exception.php&#39;;
    if (&#39;Action&#39; == substr($methodName, -6)) {
      $action = substr($methodName, 0, strlen($methodName) - 6);
      throw new Zend_Controller_Action_Exception(sprintf(&#39;Action "%s" does not exist and was not trapped in __call()&#39;, $action), 404);
    }
    throw new Zend_Controller_Action_Exception(sprintf(&#39;Method "%s" does not exist and was not trapped in __call()&#39;, $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&#39;t change the action, so we can continue
        if ($this->getInvokeArg(&#39;useCaseSensitiveActions&#39;) || in_array($action, $this->_classMethods)) {
          if ($this->getInvokeArg(&#39;useCaseSensitiveActions&#39;)) {
            trigger_error(&#39;Using case sensitive actions without word separators is deprecated; please do not rely on this "feature"&#39;);
          }
          $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 = &#39;index&#39;;
    }
    $action = $action . &#39;Action&#39;;
    $request->setDispatched(true);
    $this->dispatch($action);
    return $this->getResponse();
  }
  protected function _getParam($paramName, $default = null)
  {
    $value = $this->getRequest()->getParam($paramName);
     if ((null === $value || &#39;&#39; === $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 provides render functions for actions and views, as well as registered request and response objects, common helpers, and more.

Common methods of action controllers

The commonly used methods and attributes in action controllers are as follows:

$this->_helper mainly completes the related operations of the assistant, for example:

// 只是局部控制器;当初始化加载时,对这个控制器的所有动作有效:
$this->_helper->viewRenderer->setNoRender(true);
// 全局:
$this->_helper->removeHelper(&#39;viewRenderer&#39;);
 // 也是全局,但需要和本地版本协作,以便繁殖这个控制器:
Zend_Controller_Front::getInstance()->setParam(&#39;noViewRenderer&#39;, true);

You can simply disable rendering (rendering) for an independent view by setting the ViewRenderer's noRender flag:

class FooController extends Zend_Controller_Action
{
  public function barAction()
  {
    // disable autorendering for this action only:
    $this->_helper->viewRenderer->setNoRender();
  }
}

The main reason to disable ViewRenderer is if you don't need the view object or if you Not parsed through view scripts (for example, when using action controllers to serve web service protocols such as SOAP, XML-RPC or REST). In most cases, you don't need to disable ViewRenderer globally, just selectively disable it in individual controllers or actions.

Related operations on request objects and response objects

Countless objects and variables are registered with the object, and each has accessor methods.

Request object: getRequest() can be used to read the calling action request object.

Response object: getResponse() can be used to read the response object that collects the final response. Some typical calls look like this:

$this->getResponse()->setHeader(&#39;Content-Type&#39;, &#39;text/xml&#39;);
$this->getResponse()->appendBody($content);

Call parameters: The front-end controller may pass parameters to routers, dispatchers, and action controllers. To read these parameters, use getInvokeArg($key); alternatively, use getInvokeArgs() to read the entire parameter list.

Request parameters: Request object mobile request parameters, such as any _GET or _POST parameters, or user parameters specified in the path information of the URL. To read these parameters, use _getParam($key) or _getAllParams(). You can also use _setParam() to set request parameters; this is useful when forwarding to another action.

Use _hasParam($key) to test whether a parameter exists (useful for logical branches).

Note: _getParam() can take an optional second parameter, which contains a default value if it is not empty. Use it to eliminate calls to _hasParam() before reading the value:

// Use default value of 1 if id is not set
$id = $this->_getParam(&#39;id&#39;, 1);
// Instead of:
if ($this->_hasParam(&#39;id&#39;) {
  $id = $this->_getParam(&#39;id&#39;);
} else {
  $id = 1;
}

View related operations

Zend_Controller_Action provides an initial flexible mechanism for view inheritance. There are two methods to accomplish this: initView() and render(); the former loosely loads the $view public property, and the latter resolves the view based on the currently requested action, which uses the directory hierarchy to determine the script path.

View initialization

initView() initializes the view object. To read the view object, render() calls initView(), but it can be initialized at any time; by default, it assembles the $view property with a Zend_View object, but any class that implements Zend_View_Interface can be used. If $view has been initialized, it simply returns the properties.

The default implementation uses the following hypothetical directory structure:

applicationOrModule/
controllers/
IndexController.php
views/
scripts/
index /
Index.phtml
helpers/
Filters/

In other words. and filters). When determining the name and path of the view script, first use views/scripts/ as the base path, and then add the directory named after the controller corresponding to the view script.

Parsing (Rendering) View

render() has the following signature:

string render(string $action = null,
       string $name = null,
       bool $noController = false);

render() parsing view script. If no parameters are passed, it assumes that the requested script is [controller]/[action].phtml (.phtml is the value of the $viewSuffix attribute). Passing a value for $action will resolve the template in the [controller] subdirectory. To override with [controller], pass a true value to $noController. Finally, the template is parsed into the response object; if you wish to parse to a named segment specified in the response object, pass a value to $name.

Note: Because controller and action names may contain delimiters such as '_', '.' and '-', render() normalizes them to '-' when determining the view name. Internally, it uses the dispatcher's word and path separators for normalization. This way, a request for /foo.bar/baz-bat will parse the script foo-bar/baz-bat.phtml. If the action method contains camelCasing, remember that this will become words separated by '-' when determining the view script file name.

Some examples:

class MyController extends Zend_Controller_Action
{
  public function fooAction()
  {
    // Renders my/foo.phtml
    $this->render();
    // Renders my/bar.phtml
    $this->render(&#39;bar&#39;);
    // Renders baz.phtml
    $this->render(&#39;baz&#39;, null, true);
    // Renders my/login.phtml to the &#39;form&#39; segment of the
    // response object
    $this->render(&#39;login&#39;, &#39;form&#39;);
    // Renders site.phtml to the &#39;page&#39; segment of the response
    // object; does not use the &#39;my/&#39; subirectory
    $this->render(&#39;site&#39;, &#39;page&#39;, true);
  }
  public function bazBatAction()
  {
    // Renders my/baz-bat.phtml
    $this->render();
  }
}

Others

_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 (&#39;Action&#39; == substr($method, -6)) {
      // If the action method was not found, render the error
      // template
      return $this->render(&#39;error&#39;);
    }
    // all other methods throw an exception
    throw new Exception(&#39;Invalid method "&#39;
              . $method
              . &#39;" called&#39;,
              500);
  }
}

另外的可能性就是你可能想转发到缺省控制页面:

class MyController extends Zend_Controller_Action
{
  public function indexAction()
  {
    $this->render();
  }
  public function __call($method, $args)
  {
    if (&#39;Action&#39; == substr($method, -6)) {
      // If the action method was not found, forward to the
      // index action
      return $this->_forward(&#39;index&#39;);
    }
    // all other methods throw an exception
    throw new Exception(&#39;Invalid method "&#39;
              . $method
              . &#39;" called&#39;,
              500);
  }
}

为了定制控制器,除了重写__call()以外,本章前面说涉及的初始化、实用程序、访问器、视图和派遣钩子等方法都可以被重写。作为例子,如果把视图对象保存到注册表里,你可能想用象下面的代码来修改initView():

abstract class My_Base_Controller extends Zend_Controller_Action
{
  public function initView()
  {
    if (null === $this->view) {
      if (Zend_Registry::isRegistered(&#39;view&#39;)) {
        $this->view = Zend_Registry::get(&#39;view&#39;);
      } else {
        $this->view = new Zend_View();
        $this->view->setBasePath(dirname(__FILE__) . &#39;/../views&#39;);
      }
    }
    return $this->view;
  }
}

希望本文所述对大家PHP程序设计有所帮助。

更多Zend Framework教程之动作的基类Zend_Controller_Action详解相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn