Handling exceptions in a Front Controller plugin
If you have a Zend Framework Front Controller plugin which throws an exception, then the action is still executed and then the error action is then called, so that the displayed output shows two actions rendered, with two layouts also rendered. This is almost certainly not what you want or what you expected.
This is how to handle errors in a Front Controller plugin:
- Prefer preDispatch() over dispatchLoopStartup() as it is called from within the dispatch loop
- Catch the exception and the modify the request so that the error controller’s error action is dispatched.
- Create an error handler object so that the error action works as expected.
This is the code:
<?php
class Application_Plugin_Foo extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
try {
// do something that throws an exception
} catch (Exception $e) {
// Repoint the request to the default error handler
$request->setModuleName('default');
$request->setControllerName('error');
$request->setActionName('error');
// Set up the error handler
$error = new Zend_Controller_Plugin_ErrorHandler();
$error->type = Zend_Controller_Plugin_ErrorHandler::EXCEPTION_OTHER;
$error->request = clone($request);
$error->exception = $e;
$request->setParam('error_handler', $error);
}
}
}
That’s it.
In this case you need a try/catch for every plugin you create, which is easily forgotten.
Isn't is more comfortable to let the errors be handled by the FrontController? You need to extend the Zend_Controller_Front with My_Controller_Front and alter the calls to the plugin hooks.
Having a look at the code, I think there is a simpler way of:
$this->getResponse()->setException($e);
The plugin's are given both the request and response at an earlier point. The ErrorHandler will spot this and run the error as a controller exception would.
Actually, ignore that, it wont work correctly, can you delete the comment?
Jurian,
I only tend to have one or possibly two Front Controller plugins, so it's easy enough to add the try/catch. If I had lots, then I can definitely see the usefulness of extending the Front Controller.
Andy,
I tried that, and as you've noticed the error handler doesn't handle it. You do get a 'proper' exception though rather than multiple actions being rendered!
I've left the comment in though as it's useful information for others reading this article.
Regards,
Rob…
Great article!
I could not get preDispatch in controller plugin to work properly in case of exception but now it works perfectly so thanks!
But still, I am wondering, will there be a fix provided in the future, so the original action will not be executed if exception is thrown in preDispatch (which means try/catch will not be needed for injecting exception and redirecting)?
i commited a bugfix for this issue ZF-11561
I don't think this is a bug. When you throw an exception the error controller is created and the whole loop starts again. That means that the preDispatch/postDispatch/etc. method is triggered again and that would lead to infinity loop.
You should do something like this:
<?php
class Application_Plugin_Foo extends Zend_Controller_Plugin_Abstract
{
public static $exceptionThrown = false;
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
try {
// do something that throws an exception
} catch (Exception $e) {
if (!self::$exceptionThrown) {
self::$exceptionThrown = true;
throw $e;
}
}
}
}