Issues

ZF-5670: Fix to Front Controller and ErrorHandler helper to work when throwing an exception in the router

Description

The design of Zend_Controller_Front::dispatch() has two levels of try/catch and handles exceptions thrown in routing separately from exceptions thrown during dispatch. This means that exceptions thrown in the router will not be picked up by Zend_Controller_Plugin_ErrorHandler because the entire dispach loop never runs.

It seems valid to be able to throw exceptions during routing; in fact, the API recommends throwing a Zend_Controller_Router_Exception. But currently this prevents ErrorHandler from being able to reroute to the ErrorController.

I have a working patch that corrects this behaviour (tested against ZF 1.7.3).

It essentially wraps another try/catch around the routing functions, and alters ErrorHandler to check for exceptions in both preDispatch() and postDispatch(). preDispatch is needed to switch to the ErrorHandler before dispatching if an exception occurred during routing.

Sorry this is not in a patch format, but it is easy to apply. When I get some more time on this I'll create and upload a diff file, but in the meantime the code changes are reproduced below.

  1. Replace lines 893 - 916 in Zend_Controller_Front with the following:

            try {
                /**
                * Notify plugins of router startup
                */
                $this->_plugins->routeStartup($this->_request);

                $router->route($this->_request);

                /**
                * Notify plugins of router completion
                */
                $this->_plugins->routeShutdown($this->_request);

                /**
                 * Notify plugins of dispatch loop startup
                 */
                $this->_plugins->dispatchLoopStartup($this->_request);

            } catch (Exception $e) {
                if ($this->throwExceptions()) {
                    throw $e;
                    }
                $this->_response->setException($e);
            }
  1. And add the following into Zend_Controller_Plugin_ErrorHandler (begins line 189)

    /**
     * preDispatch() plugin hook -- check for exceptions during routing and
     * dispatches the error handler if necessary
     *
     * @see {@link Zend_Controller_Plugin_ErrorHandler::_handleException}
     * @param  Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        $this->_handleException($request);
    }


    /**
     * postDispatch() plugin hook -- check for exceptions during the dispatch
     * loop and dispatches the error handler if necessary
     *
     * @see {@link Zend_Controller_Plugin_ErrorHandler::_handleException}
     * @param  Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function postDispatch(Zend_Controller_Request_Abstract $request)
    {
        $this->_handleException($request);
    }

    /**
     * Check for exceptions and dispatch error handler if necessary
     *
     * If the 'noErrorHandler' front controller flag has been set,
     * returns early.
     *
     * @param  Zend_Controller_Request_Abstract $request
     * @return void
     */
    private function _handleException(Zend_Controller_Request_Abstract $request)
    {
        // ... code that used to be in postDispatch() goes in here verbatim
    }

Note that the current code inside postDispatch() now does into the private _handleException()

Comments

Is this still valid?