1

On a PHP webapp using the Zend 1 framework, if the code throws an exception, I get a nice error page with my branding etc.

If the code encounters a PHP fatal error (e.g. "method call on non-object" when an object reference is unexpectedly null), then I just get a bare Apache 500 error page.

How can I get a nice error page in the latter case?

Things I have tried:

  • If the PHP ini "display_errors" is set, then I get just the fatal error message as plain text
  • If "display_errors" is not set, then I get the Apache default 500 error page
  • The Apache "ErrorDocument" directive seems to be ignored in this case

2 Answers 2

2

In case this helps anyone I was able, via a bit of hackery, to get the full Zend error page (with application layout) to display in the case of fatal errors.

Hopefully there is a much easier way to do this. If so, please add an answer with your alternative.

Add this to the bootstrap:

  /**
   * Sets up a register_shutdown_function hook to give a nice error page when a
   * PHP fatal error is encountered.
   */
  protected function _initFatalErrorCatcher()
  {
      register_shutdown_function(array($this, 'onApplicationShutdown'));
  }

  public function onApplicationShutdown()
  {
      $error = error_get_last();
      $wasFatal = ($error && ($error['type'] === E_ERROR) || ($error['type'] === E_USER_ERROR));
      if ($wasFatal)
      {
          $frontController = Zend_Controller_Front::getInstance();
          $errorHandler = $frontController->getPlugin('Zend_Controller_Plugin_ErrorHandler');
          $request = $frontController->getRequest();
          $response = $frontController->getResponse();

          // Add the fatal exception to the response in a format that ErrorHandler will understand
          $response->setException(new Exception(
              "Fatal error: $error[message] at $error[file]:$error[line]",
              $error['type']));

          // Call ErrorHandler->_handleError which will forward to the Error controller
          $handleErrorMethod = new ReflectionMethod('Zend_Controller_Plugin_ErrorHandler', '_handleError');
          $handleErrorMethod->setAccessible(true);
          $handleErrorMethod->invoke($errorHandler, $request);

          // Discard any view output from before the fatal
          ob_end_clean();

          // Now display the error controller:
          $frontController->dispatch($request, $response);
      }
  }

You need a custom router class to help:

class My_Router_Rewrite extends Zend_Controller_Router_Rewrite
{
    public function route(Zend_Controller_Request_Abstract $request)
    {
        // If the ErrorHandler plugin has stashed the error in a request param, then
        // it will have already dealt with routing (see Bootstrap::onApplicationShutdown())
        // (Note that this param cannot be spoofed, since any user-supplied params
        // will be strings, not objects)
        if (is_object($request->getParam('error_handler'))) {
            return $request;
        } else {
            return parent::route($request);
        }
    }
}

(make sure that this class is registered as your router in the bootstrap)

Sign up to request clarification or add additional context in comments.

4 Comments

Doesn't this do what you need?: framework.zend.com/manual/1.12/en/zend.controller.plugins.html. Zend_Controller_Plugin_ErrorHandler handles 500 errors fine
No, "Zend_Controller_Plugin_ErrorHandler provides a drop-in plugin for handling exceptions thrown by your application" (my emphasis). I already have this plugin (it is included by default), and it only handles exceptions. Without the code shown above, it does not handle fatal errors (such as "method call on non-object" when an object reference is unexpectedly null).
I see, but you're code shouldn't be going live with errors, so the "nice" error page should never be seen, you should only get the plain text error during development :)
lol -- who needs error handling; just remember not to make any errors.
1

A solution similar to previous but without needing of rewrite router.

In Bootstrap.php:

public function __construct($application) {
    parent::__construct($application);

    // handle PHP errors
    Core_Error_Handler::set();
}

The Core/Error/Handler.php class:

class Core_Error_Handler {

    public static function handle() {

        if(!$error = error_get_last()) {
            return;
        }

        $frontController = Zend_Controller_Front::getInstance();
        $request = $frontController->getRequest();
        $response = $frontController->getResponse();

        $response->setException(new Exception("Fatal error: $error[message] at $error[file]:$error[line]",$error['type']));

        // this clean what is before the error
        ob_end_clean();

        $frontController->dispatch($request, $response);
    }

    public static function set(){
        register_shutdown_function(array(__CLASS__, 'handle'));
    }
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.