1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: 15: 16: 17: 18: 19: 20: 21: 22:
23: class Jyxo_ErrorHandler
24: {
25: 26: 27: 28: 29:
30: const NOTICE = 'notice';
31:
32: 33: 34: 35: 36:
37: const WARNING = 'warning';
38:
39: 40: 41: 42: 43:
44: const ERROR = 'error';
45:
46: 47: 48: 49: 50:
51: const FATAL = 'fatal';
52:
53: 54: 55: 56: 57:
58: const EXCEPTION = 'exception';
59:
60: 61: 62: 63: 64:
65: const STRICT = 'strict';
66:
67: 68: 69: 70: 71:
72: const DEPRECATED = 'deprecated';
73:
74: 75: 76: 77: 78:
79: private static $debug = false;
80:
81: 82: 83: 84: 85:
86: private static $errorMail;
87:
88: 89: 90: 91: 92:
93: public final function __construct()
94: {
95: throw new LogicException(sprintf('It is forbidden to create instances of %s class.', get_class($this)));
96: }
97:
98: 99: 100: 101: 102:
103: public static function init($debug = false)
104: {
105:
106: self::$debug = (bool) $debug;
107:
108:
109: set_error_handler(array(__CLASS__, 'handleError'));
110: set_exception_handler(array(__CLASS__, 'handleException'));
111: register_shutdown_function(array(__CLASS__, 'handleFatalError'));
112: }
113:
114: 115: 116: 117: 118:
119: public static function setErrorMail(Jyxo_ErrorMail $errorMail)
120: {
121: self::$errorMail = $errorMail;
122: }
123:
124: 125: 126: 127: 128: 129: 130: 131: 132: 133:
134: public static function handleError($type, $message, $file, $line, $context)
135: {
136:
137: if (0 === ($type & error_reporting())) {
138: return true;
139: }
140:
141: static $types = array(
142: E_RECOVERABLE_ERROR => self::ERROR,
143: E_USER_ERROR => self::ERROR,
144: E_WARNING => self::WARNING,
145: E_USER_WARNING => self::WARNING,
146: E_NOTICE => self::NOTICE,
147: E_USER_NOTICE => self::NOTICE,
148: E_STRICT => self::STRICT,
149: E_DEPRECATED => self::DEPRECATED,
150: E_USER_DEPRECATED => self::DEPRECATED
151: );
152:
153:
154: return self::log(
155: array(
156: 'type' => $types[$type],
157: 'text' => $message,
158: 'file' => $file,
159: 'line' => $line,
160: 'context' => $context,
161: 'trace' => array_slice(debug_backtrace(), 1)
162: )
163: );
164: }
165:
166: 167: 168: 169: 170:
171: public static function handleException(Exception $exception)
172: {
173: self::exception($exception);
174: if (self::$errorMail) {
175: self::$errorMail->send($exception);
176: }
177: }
178:
179: 180: 181:
182: public static function handleFatalError()
183: {
184:
185: static $fatalErrors = array(
186: E_ERROR => true,
187: E_CORE_ERROR => true,
188: E_COMPILE_ERROR => true,
189: E_PARSE => true,
190: );
191:
192:
193: $error = error_get_last();
194: if (isset($fatalErrors[$error['type']])) {
195: self::log(
196: array(
197: 'type' => self::FATAL,
198: 'text' => $error['message'],
199: 'file' => $error['file'],
200: 'line' => $error['line']
201: )
202: );
203: if (self::$errorMail) {
204: $ex = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
205: self::$errorMail->send($ex);
206: }
207: }
208: }
209:
210: 211: 212: 213: 214: 215:
216: public static function exception(Exception $exception, $fire = true)
217: {
218: self::log(
219: array(
220: 'type' => self::EXCEPTION,
221: 'text' => $exception->getMessage() . ' [' . $exception->getCode() . ']',
222: 'file' => $exception->getFile(),
223: 'line' => $exception->getLine(),
224: 'trace' => $exception->getTrace(),
225: 'previous' => self::getAllPreviousExceptions($exception)
226: ),
227: $fire
228: );
229: }
230:
231: 232: 233: 234: 235: 236: 237:
238: public static function log(array $message, $fire = true)
239: {
240:
241: if (!isset($message['file'])) {
242: $message['file'] = null;
243: }
244: if (!isset($message['line'])) {
245: $message['line'] = null;
246: }
247: if (!isset($message['trace'])) {
248: $message['trace'] = array();
249: }
250: if (!isset($message['previous'])) {
251: $message['previous'] = array();
252: }
253:
254:
255: if (ini_get('html_errors')) {
256: $message['text'] = html_entity_decode(strip_tags($message['text']));
257: }
258:
259:
260: if (!empty($_SERVER['argv'])) {
261:
262: $request = implode(' ', $_SERVER['argv']);
263: } else {
264:
265: $request = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
266: $request .= $_SERVER['REQUEST_URI'];
267: }
268:
269:
270: $text = sprintf('%s [Request: %s]', $message['text'], $request);
271: if (isset($message['file'], $message['line'])) {
272: $text .= sprintf(' [%s: %s]', $message['file'], $message['line']);
273: }
274: $log = sprintf("%s: %s\n", strtoupper($message['type']), $text);
275:
276:
277: $log .= self::getTraceLog($message['trace']);
278:
279:
280: $previousTrace = $message['trace'];
281: foreach ($message['previous'] as $previous) {
282:
283: $trace = array_reverse(array_diff_key(array_reverse($previous->getTrace()), array_reverse($previousTrace)));
284: $previousTrace = $previous->getTrace();
285:
286: $log .= sprintf("Previous: %s [%s] [%s: %s]\n", $previous->getMessage(), $previous->getCode(), $previous->getFile(), $previous->getLine());
287: $log .= self::getTraceLog($trace);
288: }
289:
290:
291: if (self::$debug && $fire) {
292: self::firephp($message);
293: }
294:
295:
296: return error_log(trim($log));
297: }
298:
299: 300: 301: 302: 303: 304:
305: private static function firephp($message)
306: {
307: static $labels = array(
308: self::EXCEPTION => 'Exception',
309: self::FATAL => 'Fatal Error',
310: self::ERROR => 'Error',
311: self::WARNING => 'Warning',
312: self::NOTICE => 'Notice',
313: self::STRICT => 'Strict',
314: self::DEPRECATED => 'Deprecated'
315: );
316:
317:
318: return Jyxo_FirePhp::trace(
319: sprintf('%s: %s', $labels[$message['type']], $message['text']),
320: $message['file'],
321: $message['line'],
322: $message['trace']
323: );
324: }
325:
326: 327: 328: 329: 330: 331:
332: private static function getTraceLog(array $trace)
333: {
334: $log = '';
335: foreach ($trace as $levelNo => $level) {
336: if (!isset($level['file'])) {
337: $level['file'] = 0;
338: }
339: if (!isset($level['line'])) {
340: $level['line'] = 0;
341: }
342: if (!isset($level['class'])) {
343: $level['class'] = '';
344: }
345: if (!isset($level['type'])) {
346: $level['type'] = '';
347: }
348: if (!isset($level['function'])) {
349: $level['function'] = '';
350: }
351: $log .= sprintf("\t%s\t%s\t%s\t%s\n", $levelNo, $level['file'], $level['line'], $level['class'] . $level['type'] . $level['function']);
352: }
353: return $log;
354: }
355:
356: 357: 358: 359: 360: 361:
362: private static function getAllPreviousExceptions(Exception $exception)
363: {
364: $stack = array();
365: $previous = $exception->getPrevious();
366: while (null !== $previous) {
367: $stack[] = $previous;
368: $previous = $previous->getPrevious();
369: }
370: return $stack;
371: }
372: }
373: