| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
* This file is part of Raven. |
| 5 |
* |
| 6 |
* (c) Sentry Team |
| 7 |
* |
| 8 |
* For the full copyright and license information, please view the LICENSE |
| 9 |
* file that was distributed with this source code. |
| 10 |
*/ |
| 11 |
|
| 12 |
/** |
| 13 |
* Event handlers for exceptions and errors |
| 14 |
* |
| 15 |
* $client = new Raven_Client('http://public:secret/example.com/1'); |
| 16 |
* $error_handler = new Raven_ErrorHandler($client); |
| 17 |
* $error_handler->registerExceptionHandler(); |
| 18 |
* $error_handler->registerErrorHandler(); |
| 19 |
* $error_handler->registerShutdownFunction(); |
| 20 |
* |
| 21 |
* @package raven |
| 22 |
*/ |
| 23 |
|
| 24 |
// TODO(dcramer): deprecate default error types in favor of runtime configuration |
| 25 |
// unless a reason can be determined that making them dynamic is better. They |
| 26 |
// currently are not used outside of the fatal handler. |
| 27 |
class Raven_ErrorHandler |
| 28 |
{ |
| 29 |
protected $old_exception_handler; |
| 30 |
protected $call_existing_exception_handler = false; |
| 31 |
protected $old_error_handler; |
| 32 |
protected $call_existing_error_handler = false; |
| 33 |
protected $reservedMemory; |
| 34 |
/** @var Raven_Client */ |
| 35 |
protected $client; |
| 36 |
protected $send_errors_last = false; |
| 37 |
protected $fatal_error_types = array( |
| 38 |
E_ERROR, |
| 39 |
E_PARSE, |
| 40 |
E_CORE_ERROR, |
| 41 |
E_CORE_WARNING, |
| 42 |
E_COMPILE_ERROR, |
| 43 |
E_COMPILE_WARNING, |
| 44 |
E_STRICT, |
| 45 |
); |
| 46 |
|
| 47 |
/** |
| 48 |
* @var array |
| 49 |
* Error types which should be processed by the handler. |
| 50 |
* A 'null' value implies "whatever error_reporting is at time of error". |
| 51 |
*/ |
| 52 |
protected $error_types = null; |
| 53 |
|
| 54 |
public function __construct($client, $send_errors_last = false, $error_types = null, |
| 55 |
$__error_types = null) |
| 56 |
{ |
| 57 |
// support legacy fourth argument for error types |
| 58 |
if ($error_types === null) { |
| 59 |
$error_types = $__error_types; |
| 60 |
} |
| 61 |
|
| 62 |
$this->client = $client; |
| 63 |
$this->error_types = $error_types; |
| 64 |
$this->fatal_error_types = array_reduce($this->fatal_error_types, array($this, 'bitwiseOr')); |
| 65 |
if ($send_errors_last) { |
| 66 |
$this->send_errors_last = true; |
| 67 |
$this->client->store_errors_for_bulk_send = true; |
| 68 |
} |
| 69 |
} |
| 70 |
|
| 71 |
public function bitwiseOr($a, $b) |
| 72 |
{ |
| 73 |
return $a | $b; |
| 74 |
} |
| 75 |
|
| 76 |
public function handleException($e, $isError = false, $vars = null) |
| 77 |
{ |
| 78 |
$e->event_id = $this->client->captureException($e, null, null, $vars); |
| 79 |
|
| 80 |
if (!$isError && $this->call_existing_exception_handler) { |
| 81 |
if ($this->old_exception_handler !== null) { |
| 82 |
call_user_func($this->old_exception_handler, $e); |
| 83 |
} else { |
| 84 |
throw $e; |
| 85 |
} |
| 86 |
} |
| 87 |
} |
| 88 |
|
| 89 |
public function handleError($type, $message, $file = '', $line = 0, $context = array()) |
| 90 |
{ |
| 91 |
// http://php.net/set_error_handler |
| 92 |
// The following error types cannot be handled with a user defined function: E_ERROR, |
| 93 |
// E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and |
| 94 |
// most of E_STRICT raised in the file where set_error_handler() is called. |
| 95 |
|
| 96 |
if (error_reporting() !== 0) { |
| 97 |
$error_types = $this->error_types; |
| 98 |
if ($error_types === null) { |
| 99 |
$error_types = error_reporting(); |
| 100 |
} |
| 101 |
if ($error_types & $type) { |
| 102 |
$e = new ErrorException($message, 0, $type, $file, $line); |
| 103 |
$this->handleException($e, true, $context); |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
if ($this->call_existing_error_handler) { |
| 108 |
if ($this->old_error_handler !== null) { |
| 109 |
return call_user_func( |
| 110 |
$this->old_error_handler, |
| 111 |
$type, |
| 112 |
$message, |
| 113 |
$file, |
| 114 |
$line, |
| 115 |
$context |
| 116 |
); |
| 117 |
} else { |
| 118 |
return false; |
| 119 |
} |
| 120 |
} |
| 121 |
return true; |
| 122 |
} |
| 123 |
|
| 124 |
public function handleFatalError() |
| 125 |
{ |
| 126 |
unset($this->reservedMemory); |
| 127 |
|
| 128 |
if (null === $error = error_get_last()) { |
| 129 |
return; |
| 130 |
} |
| 131 |
|
| 132 |
if ($this->shouldCaptureFatalError($error['type'])) { |
| 133 |
$e = new ErrorException( |
| 134 |
@$error['message'], 0, @$error['type'], |
| 135 |
@$error['file'], @$error['line'] |
| 136 |
); |
| 137 |
$this->handleException($e, true); |
| 138 |
} |
| 139 |
} |
| 140 |
|
| 141 |
public function shouldCaptureFatalError($type) |
| 142 |
{ |
| 143 |
// Do not capture E_ERROR since those can be caught by userland since PHP 7.0 |
| 144 |
// E_ERROR should already be handled by the exception handler |
| 145 |
// This prevents duplicated exceptions in PHP 7.0+ |
| 146 |
if (PHP_VERSION_ID >= 70000 && $type === E_ERROR) { |
| 147 |
return false; |
| 148 |
} |
| 149 |
|
| 150 |
return $type & $this->fatal_error_types; |
| 151 |
} |
| 152 |
|
| 153 |
/** |
| 154 |
* Register a handler which will intercept unhandled exceptions and report them to the |
| 155 |
* associated Sentry client. |
| 156 |
* |
| 157 |
* @param bool $call_existing Call any existing exception handlers after processing |
| 158 |
* this instance. |
| 159 |
* @return Raven_ErrorHandler |
| 160 |
*/ |
| 161 |
public function registerExceptionHandler($call_existing = true) |
| 162 |
{ |
| 163 |
$this->old_exception_handler = set_exception_handler(array($this, 'handleException')); |
| 164 |
$this->call_existing_exception_handler = $call_existing; |
| 165 |
return $this; |
| 166 |
} |
| 167 |
|
| 168 |
/** |
| 169 |
* Register a handler which will intercept standard PHP errors and report them to the |
| 170 |
* associated Sentry client. |
| 171 |
* |
| 172 |
* @param bool $call_existing Call any existing errors handlers after processing |
| 173 |
* this instance. |
| 174 |
* @param array $error_types All error types that should be sent. |
| 175 |
* @return Raven_ErrorHandler |
| 176 |
*/ |
| 177 |
public function registerErrorHandler($call_existing = true, $error_types = null) |
| 178 |
{ |
| 179 |
if ($error_types !== null) { |
| 180 |
$this->error_types = $error_types; |
| 181 |
} |
| 182 |
$this->old_error_handler = set_error_handler(array($this, 'handleError'), E_ALL); |
| 183 |
$this->call_existing_error_handler = $call_existing; |
| 184 |
return $this; |
| 185 |
} |
| 186 |
|
| 187 |
/** |
| 188 |
* Register a fatal error handler, which will attempt to capture errors which |
| 189 |
* shutdown the PHP process. These are commonly things like OOM or timeouts. |
| 190 |
* |
| 191 |
* @param int $reservedMemorySize Number of kilobytes memory space to reserve, |
| 192 |
* which is utilized when handling fatal errors. |
| 193 |
* @return Raven_ErrorHandler |
| 194 |
*/ |
| 195 |
public function registerShutdownFunction($reservedMemorySize = 10) |
| 196 |
{ |
| 197 |
register_shutdown_function(array($this, 'handleFatalError')); |
| 198 |
|
| 199 |
$this->reservedMemory = str_repeat('x', 1024 * $reservedMemorySize); |
| 200 |
return $this; |
| 201 |
} |
| 202 |
} |
| 203 |
|