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