13

I have a script with an exception handler. This exception handler cleans up a couple connections, prior to the script exiting after an exception.

I would like to re-throw the exception from this exception handler so that it is handled by PHP's own last-resort exception handler, where the error is written to PHP's error log, or whatever the default is, as configured in PHP.ini.

Unfortunately, this doesn't seem like a possibility, as outlined here:

http://www.php.net/manual/en/function.set-exception-handler.php#68712

Will cause a Fatal error: Exception thrown without a stack frame

Is there another way to bubble the error up the stack so that PHP handles it after my exception handler is done cleaning up?

Brad
  • 159,648
  • 54
  • 349
  • 530
  • 1
    Using a custom exception handler will still trigger a fatal error and thus log the error. – netcoder Oct 26 '11 at 02:45
  • Why don't you [just throw the exception again](http://stackoverflow.com/questions/7856173/throwing-exception-within-exception-handler/7939492#7939492) from your handler? It works if you know how. And it will triggers PHP last resort handler you're looking for. – hakre Oct 30 '11 at 13:45
  • I've seen this before (and would recommend restoring the error handler) but I can't reproduce right now! Is this issue partial to a version of PHP / .ini settings? – jlb Oct 31 '11 at 09:41

5 Answers5

18

You can not re-throw from the exception handler, however, there are other places you can. For example you can de-couple the re-throw from the handler by encapsulating things into a class of it's own and then use the __destruct() function (PHP 5.3, Demo):

<?php

class ExceptionHandler
{
    private $rethrow;
    public function __construct()
    {
        set_exception_handler(array($this, 'handler'));
    }
    public function handler($exception)
    {
        echo  "cleaning up.\n";
        $this->rethrow = $exception;
    }
    public function __destruct()
    {
        if ($this->rethrow) throw $this->rethrow;
    }
}

$handler = new ExceptionHandler;

throw new Exception();

Put this into my error log:

[29-Oct-2011 xx:32:25] PHP Fatal error: Uncaught exception 'Exception' in /.../test-exception.php:23
Stack trace:
#0 {main}
thrown in /.../test-exception.php on line 23
hakre
  • 193,403
  • 52
  • 435
  • 836
  • 1
    See as well, which is somewhat related about the `__destruct`: [How to determine that a PHP script is in termination phase?](http://stackoverflow.com/q/6227611/367456) – hakre Oct 30 '11 at 13:46
  • Thanks @hakre, your solution works. More of a workaround than I was hoping for, but I do understand why this is necessary. – Brad Oct 31 '11 at 22:02
  • Hey Brad, thanks for the acceptance. I like this "workaround" more than the suggestion on the user-comments on php.net to create the error log entry on ones own. Why re-invent the wheel? I tried also with register shutdown function, but this one here I like better. – hakre Oct 31 '11 at 22:18
8

Just catch the exception and log the message yourself, then rethrow.

try {
    $foo->doSomethingToCauseException();
} catch (Exception $e) {
    error_log($e->getMessage());
    throw $e;
}

If you bubble up to the top and PHP is unable to handle, it will result in uncaught exception.

Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • An uncaught exception would be fine with me. I want to bubble all the up to the top. I am error logging myself right now (using the exact method you are describing), but would rather let PHP handle it, for consistency. – Brad Oct 21 '11 at 23:30
  • I see. I'm curious about this as well. In my experience the only way PHP handles exceptions is by causing the page to explode, lol. – Mike Purcell Oct 21 '11 at 23:31
3

Will cause a Fatal error: Exception thrown without a stack frame

This error means that your exception is thrown from a code that is not part of the script (as far as PHP knows). Examples of such code include custom exception handler set with set_exception_handler() and any class destructor method. There's no choice but to NOT throw an exception from such a code.

If you want PHP native error handling, I'd suggest you to call trigger_error() instead. It should log the error if you don't have custom error handler and you use suitable error type. For example, E_USER_ERROR should be fine.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
  • Or [just throw the exception again](http://stackoverflow.com/questions/7856173/throwing-exception-within-exception-handler/7939492#7939492) which depending on configuration will give the proper backtrace. – hakre Oct 30 '11 at 13:43
  • hakre: just try it (throw *anything* from inside a custom exception handler or class destructor method) -- you'll find that it does not work. It does not matter if you use xdebug or not. – Mikko Rantalainen Oct 31 '11 at 10:58
  • 1
    Mikko, looks like [my answer](http://stackoverflow.com/questions/7856173/throwing-exception-within-exception-handler/7939492#7939492) requires PHP 5.3. See as well http://codepad.viper-7.com/jamrqP – hakre Oct 31 '11 at 11:02
2

Just rethrow the exception as a RunTimeException and it will keep the stacktrace :)

try {
    // bad exception throwing code
} catch (Exception $e) {
    throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}
aqm
  • 2,942
  • 23
  • 30
0

From http://www.php.net/manual/en/function.set-exception-handler.php#88082 i read: Another solution is to restore the error handler at the beginning of the exception handler. Have you tried it?

user1014351
  • 437
  • 1
  • 5
  • 16
  • This doesn't work, sorry. The `restore_exception_handler()` has no effect within an exception. – Brad Oct 31 '11 at 22:00