2.3. Subclassing
Prev | Chapter 2. Zend_Controller | Next

## 2.3. Subclassing

### 2.3.1. Introduction

The `Zend_Controller` system was built with extensibility in mind, either by subclassing the existing classes or writing new classes that implement the interfaces `Zend_Controller_Router_Interface` and `Zend_Controller_Dispatcher_Interface` or extend the classes `Zend_Controller_Request_Abstract`, `Zend_Controller_Response_Abstract`, and `Zend_Controller_Action`.

Possible reasons for subclassing include:

- The existing URI routing system is not suitable in some way, such as integrating into an existing website that uses its own conventions for routing that do not mesh with the routing mechanism provided with the Zend Framework.

- You need to implement routing for something completely different. The `Zend_Controller_Router` class only deals with URIs. It's possible and likely that you might want to use the MVC pattern for developing another type of program, such as a console or GUI application. In the case of a console application, a custom request object could process command line arguments.

- The mechanism provided by `Zend_Controller_Dispatcher` is not suitable. The default configuration assumes a convention that controllers are classes and actions are methods of those classes. However, there are many other strategies for doing this. One example would be where controllers are directories and actions are files within those directories.

- You wish to provide additional capabilities that will be inherited by all of your controllers. For example, `Zend_Controller_Action` does not integrate with `Zend_View` by default. However, you could extend your own controller to do this, and using it would not require modifying the supplied `Zend_Controller_Router` or `Zend_Controller_Dispatcher`.

- You wish to log application exceptions when caught and redirect to a generic error page. Extending `Zend_Controller_Response_Http`, you could modify `__toString()` to check for registered exceptions, log them, and then redirect to an error page.

Please be careful when overriding significant parts of the system, particularly the dispatcher. One of the advantages of `Zend_Controller` is that it establishes common conventions for building applications. If too much of this default behavior is changed, some of these advantages are lost. However, there are many different needs and one solution can't fit them all, so the freedom is provided if needed.

### 2.3.2. Conventions

When subclassing any of the Zend_Controller classes, you are strongly encouraged to follow these conventions for naming or storing files. Doing so will ensure that another programmer who is familiar with the Zend Framework will be able to understand your project easily.

#### 2.3.2.1. Prefix

Classes included with the Zend Framework follow a convention where every class is prefixed by "Zend_". This is the prefix. We recommend that you name all of your classes in the same way, e.g. if your company name is Widget, Inc., the prefix might be "Widget_".

#### 2.3.2.2. Directory Layout

The `Zend_Controller` classes are stored in the library directory as follows:

    /library
      /Zend
        /Controller
          Action.php
          Dispatcher.php
          Router.php

When subclassing `Zend_Controller` classes, it is recommended that the new classes be stored in an identical structure under your prefix. This will make them easy to find for someone during that learning process of reviewing the code for your project.

For example, a project from Widget, Inc. which implements only a custom router might look like this:

    /library
      /Zend
      /Widget
        /Controller
          Router.php
          README.txt

Notice in this example that the `Widget/Controller/` directory mirrors the `Zend/Controller/` directory wherever possible. In this case, it provides the class `Widget_Controller_Router`, which would be either a subclass or replacement for `Zend_Controller_Router` implementing `Zend_Controller_Router_Interface`.

Also, notice in the example above that a `README.txt` file has been placed in `Widget/Controller/`. Zend strongly encourages you to document your projects through supplying separate tests and documentation for customers. However, we encourage you to also place a simple `README.txt` file right in the directory to briefly explain your changes and how they work.

### 2.3.3. Request Abstract

The abstract `Zend_Controller_Request_Abstract` defines a handful of methods:

        /**
         * @return string
         */
        public function getControllerName();

        /**
         * @param string $value
         * @return self
         */
        public function setControllerName($value);

        /**
         * @return string
         */
        public function getActionName();

        /**
         * @param string $value
         * @return self
         */
        public function setActionName($value);

        /**
         * @return string
         */
        public function getControllerKey();

        /**
         * @param string $key
         * @return self
         */
        public function setControllerKey($key);

        /**
         * @return string
         */
        public function getActionKey();

        /**
         * @param string $key
         * @return self
         */
        public function setActionKey($key);

        /**
         * @param string $key
         * @return mixed
         */
        public function getParam($key);

        /**
         * @param string $key
         * @param mixed $value
         * @return self
         */
        public function setParam($key, $value);

        /**
         * @return array
         */
         public function getParams();

        /**
         * @param array $array
         * @return self
         */
        public function setParams(array $array);

        /**
         * @param boolean $flag
         * @return self
         */
        public function setDispatched($flag = true);

        /**
         * @return boolean
         */
        public function isDispatched();
    }

The request object is a container for the request environment. The controller chain really only needs to know how to set and retrieve the controller, action, optional parameters, and dispatched status. By default, the request will search its own parameters using the controller or action keys in order to determine the controller and action.

### 2.3.4. Router Interface

The interface `Zend_Controller_Router_Interface` provides a definition for only one method:

    <?php

      /**
       * @param  Zend_Controller_Request_Abstract $request
       * @throws Zend_Controller_Router_Exception
       * @return Zend_Controller_Request_Abstract
       */
      public function route(Zend_Controller_Request_Abstract $request);

    ?>

Routing only occurs once: when the request is first received into the system. The purpose of the router is to determine the controller, action, and optional parameters based on the request environment, and then set them in the request. The request object is then passed to the dispatcher. If it is not possible to map a route to a dispatch token, the router should do nothing to the request object.

### 2.3.5. Dispatcher Interface

`Zend_Controller_Front` will first call the router to determine the first dispatchable action in the request. It then enters a dispatch loop.

In the loop, it first sets the request object's dispatched flag and then dispatches the request (instantiate the controller, call its action). If the action method (or a pre/postDispatch plugin) resets the request object's dispatched flag, the front controller will do another iteration of the dispatch loop with whatever action is currently set in the request object. This allows for actions to be processed sequentially until all work has been done.

The interface `Zend_Controller_Dispatcher_Interface` provides definitions for two methods:

    <?php

    /**
     * @param  Zend_Controller_Request_Abstract $request
     * @return boolean
     */
    public function isDispatchable(Zend_Controller_Request_Abstract $request);

    ?>

`isDispatchable()` checks if a request is dispatchable. If it is, it returns `TRUE`. Otherwise, it returns `FALSE`. The definition of what is dispatchable is left to the class implementing the interface. In the case of the default implementation, `Zend_Controller_Dispatcher`, it means that the controller file exists, the class exists within the file, and the action method exists within the class.

    <?php

    /**
     * @param  Zend_Controller_Request_Abstract $route
     * @return Zend_Controller_Request_Abstract
     */
    public function dispatch(Zend_Controller_Request_Abstract $request);

    ?>

`dispatch()` is where the work gets done. This method must execute the action of the controller. It must return a request object.

### 2.3.6. Action Controller

The Action Controller handles the various actions of an application. This abstract class provides the following methods:

        /**
         * @return void
         */
        public function init();

        /**
         * @return Zend_Controller_Request_Abstract
         */
        public function getRequest();

        /**
         * @param Zend_Controller_Request_Abstract $request
         * @return self
         */
        public function setRequest(Zend_Controller_Request_Abstract $request);

        /**
         * @return Zend_Controller_Response_Abstract
         */
        public function getResponse();

        /**
         * @param Zend_Controller_Response_Abstract $response
         * @return self
         */
        public function setResponse(Zend_Controller_Response_Abstract $response);

        /**
         * @return array
         */
        public function getInvokeArgs();

        public function preDispatch();

        public function postDispatch();

        /**
         * @param string $methodName
         * @param array $args
         */
        public function __call($methodName, $args);

        /**
         * @param null|Zend_Controller_Request_Abstract $request Optional request
         * object to use
         * @param null|Zend_Controller_Response_Abstract $response Optional response
         * object to use
         * @return Zend_Controller_Response_Abstract
         */
        public function run(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null);

The constructor is final, to ensure that therequest and response object are available at object instantiation, and that they are registered with the object. While it only specifies two arguments, any parameter registered in the Front Controller using `addParam()` or `setParams()` will be passed, in order, following these arguments. They are then registered in the object and passed to `init()`. A developer may override the `init()` method to provide further initialization-time actions.

When an action is dispatched, processing may be performed before and after the action using the `preDispatch()` and `postDispatch()` methods, respectively. By default, they are empty and do nothing.

The `__call()` method will handle any unregistered actions in the class. By default, it throws an exception if the action is not defined. This should only ever occur if the default action method is not defined.

The default naming convention for action methods is lowercaseAction, where 'lowercase' specifies the name of the action, and 'Action' specifies that the method is an action method. Thus, `http://framework.zend.com/foo/bar` will call `FooController::barAction()`.

Action controllers may also be used as Page Controllers. Most typical usage would be as follows:

    $controller = new FooController(
        new Zend_Controller_Request_Abstract(),
        new Zend_Controller_Response_Abstract()
    );
    $controller->run();

| Note
We recommend using the Front Controller/Action Controller combination instead of the Page Controller paradigm to encourage writing applications that will inter-operate.

### 2.3.7. Response Object

The Response Object collects content and headers from the various actions called and returns them to the client. It has the following methods:

        /**
         * @param string $content
         * @return self
         */
        public function setBody($content);

        /**
         * @param string $content
         * @return self
         */
        public function appendBody($content);

        /**
         * @return string
         */
        public function getBody();

        /**
         * @param Exception $e
         * @return self
         */
        public function setException(Exception $e);

        /**
         * @return null|Exception
         */
        public function getException();

        /**
         * @return boolean
         */
        public function isException();

        /**
         * @param boolean $flag
         * @return boolean
         */
        public function renderExceptions($flag = null);

        /**
         * @return string
         */
        public function __toString();

`setBody()` will replace all body content; we encourage using `appendBody()` instead. `__toString()` should render any content.

The response object is also where action controller exceptions are finally caught and registered. `isException()` should return a boolean indicating whether or not this has occurred. `renderExceptions()` should be used to indicate whether `__toString()` will render exception output if an exception was trapped.

Prev | Up | Next
2.2. Getting Started | Home | 2.4. Provided Subclasses