<?php
class Raven_ErrorHandler
{
protected $old_exception_handler;
protected $call_existing_exception_handler = false;
protected $old_error_handler;
protected $call_existing_error_handler = false;
protected $reservedMemory;
protected $client;
protected $send_errors_last = false;
protected $fatal_error_types = array(
E_ERROR,
E_PARSE,
E_CORE_ERROR,
E_CORE_WARNING,
E_COMPILE_ERROR,
E_COMPILE_WARNING,
E_STRICT,
);
protected $error_types = null;
private $lastHandledException;
public function __construct($client, $send_errors_last = false, $error_types = null,
$__error_types = null)
{
if ($error_types === null) {
$error_types = $__error_types;
}
$this->client = $client;
$this->error_types = $error_types;
$this->fatal_error_types = array_reduce($this->fatal_error_types, array($this, 'bitwiseOr'));
if ($send_errors_last) {
$this->send_errors_last = true;
$this->client->store_errors_for_bulk_send = true;
}
}
public function bitwiseOr($a, $b)
{
return $a | $b;
}
public function handleException($e, $isError = false, $vars = null)
{
$e->event_id = $this->client->captureException($e, null, null, $vars);
$this->lastHandledException = $e;
if (!$isError && $this->call_existing_exception_handler) {
if ($this->old_exception_handler !== null) {
call_user_func($this->old_exception_handler, $e);
} else {
throw $e;
}
}
}
public function handleError($type, $message, $file = '', $line = 0, $context = array())
{
if (error_reporting() !== 0) {
$error_types = $this->error_types;
if ($error_types === null) {
$error_types = error_reporting();
}
if ($error_types & $type) {
$e = new ErrorException($message, 0, $type, $file, $line);
$this->handleException($e, true, $context);
}
}
if ($this->call_existing_error_handler) {
if ($this->old_error_handler !== null) {
return call_user_func(
$this->old_error_handler,
$type,
$message,
$file,
$line,
$context
);
} else {
return false;
}
}
return true;
}
public function handleFatalError()
{
unset($this->reservedMemory);
if (null === $error = error_get_last()) {
return;
}
if ($this->shouldCaptureFatalError($error['type'], $error['message'])) {
$e = new ErrorException(
@$error['message'], 0, @$error['type'],
@$error['file'], @$error['line']
);
$this->client->useCompression = $this->client->useCompression && PHP_VERSION_ID > 70000;
$this->handleException($e, true);
}
}
public function shouldCaptureFatalError($type, $message = null)
{
if (PHP_VERSION_ID >= 70000 && $this->lastHandledException) {
if ($type === E_CORE_ERROR && strpos($message, 'Exception thrown without a stack frame') === 0) {
return false;
}
if ($type === E_ERROR) {
$expectedMessage = 'Uncaught '
. \get_class($this->lastHandledException)
. ': '
. $this->lastHandledException->getMessage();
if (strpos($message, $expectedMessage) === 0) {
return false;
}
}
}
return (bool) ($type & $this->fatal_error_types);
}
public function registerExceptionHandler($call_existing = true)
{
$this->old_exception_handler = set_exception_handler(array($this, 'handleException'));
$this->call_existing_exception_handler = $call_existing;
return $this;
}
public function registerErrorHandler($call_existing = true, $error_types = null)
{
if ($error_types !== null) {
$this->error_types = $error_types;
}
$this->old_error_handler = set_error_handler(array($this, 'handleError'), E_ALL);
$this->call_existing_error_handler = $call_existing;
return $this;
}
public function registerShutdownFunction($reservedMemorySize = 10)
{
register_shutdown_function(array($this, 'handleFatalError'));
$this->reservedMemory = str_repeat('x', 1024 * $reservedMemorySize);
return $this;
}
}