rollbar/rollbar-php — pull request #18 by alexthegeek (2014-03-20 11:35:49+00:00)
Title: Factoring fatal error handling out of flush function

Our team are using this library to catch errors and report them to Rollbar, but only on certain environments. On others, we use different reporting methods. As such, we use our own error handler which sits in front of Rollbar, and passes on any errors it catches to Rollbar where appropriate. This is easy to do as we can pass `false` as the second and third parameters of the `Rollbar::init()` function to prevent it from registering it's own error and exception handlers.

The only problem with this approach is that this doesn't apply to fatal errors - there is no way currently to stop the `Rollbar` object from intercepting and reporting fatal errors independently. Another issue is that fatal errors are only reported if batching is switched on.

Fortunately, [register_shutdown_function is stackable](http://www.php.net/manual/en/function.register-shutdown-function.php), meaning we can register multiple functions to run in order at shutdown. As such, I've split the fatal error catching code out from the `Rollbar::flush()` function, and added an extra parameter to the end of the `Rollbar::init()` function, mimicking the behaviour of the other two handlers.

## Potential Gotcha: Registered shutdown functions are called in order

Provided our own shutdown function is registered __before__ the Rollbar library is initialized, `E_ERROR` will be reported:

```php
class Custom_Error_Handler {
public static function init() {
set_exception_handler('Custom_Error_Handler::process_exception');
set_error_handler('Custom_Error_Handler::process_error');
register_shutdown_function('Custom_Error_Handler::catch_fatal_error');
Rollbar::init(array(/* rollbar token etc */), false, false, false);
}

public static function process_exception(Exception $exc) {

// ...

Rollbar::report_exception($exc);
}

public static function process_error($errno, $errmsg, $errfile, $errline) {

// ...

Rollbar::report_php_error($errno, $errmsg, $errfile, $errline);
}

public static function catch_fatal_error() {
$last_error = error_get_last();
if(!is_null($last_error) && $last_error['type'] == E_ERROR) {
self::process_error($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
}
}
}

$not_an_object = 'just a string';
$not_an_object->not_a_method(); // Generates E_ERROR
/*
Registered shutdown functions fire in the order they were registered:

Custom_Error_Handler::catch_fatal_error() fires
Rollbar::report_fatal_error() wasn't registered, because we passed in false
Rollbar::flush() fires (assuming the library is in batch mode), and sends the error to Rollbar
*/
```

Note that if Rollbar was initialized before `Custom_Error_Handler::catch_fatal_error` was registered as a shutdown function, the final `Rollbar::flush()` will occur __before__ our `catch_fatal_error` has passed the error on to Rollbar. In this situation, in batch mode, the error may not be reported to Rollbar.

## Backwards compatibility

Like the other two parameters, the new parameter has a default value of true, so should be backwards compatible, and also solves the issue of fatal errors not being reported when batched mode is not being used.