1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22: 23:
24: final class Debug
25: {
26:
27: public static $productionMode;
28:
29:
30: public static $consoleMode;
31:
32:
33: public static $time;
34:
35:
36: private static $firebugDetected;
37:
38:
39: private static $ajaxDetected;
40:
41:
42: public static $source;
43:
44:
45:
46:
47: public static $maxDepth = 3;
48:
49:
50: public static $maxLen = 150;
51:
52:
53: public static $showLocation = FALSE;
54:
55:
56:
57:
58: const DEVELOPMENT = FALSE;
59: const PRODUCTION = TRUE;
60: const DETECT = NULL;
61:
62:
63:
64: public static $strictMode = FALSE; 65:
66:
67: public static $scream = FALSE;
68:
69:
70: public static $onFatalError = array();
71:
72:
73: public static $logDirectory;
74:
75:
76: public static $email;
77:
78:
79: public static $mailer = array(__CLASS__, 'defaultMailer');
80:
81:
82: public static $emailSnooze = 172800;
83:
84:
85: public static $editor = 'editor://open/?file=%file&line=%line';
86:
87:
88: private static $enabled = FALSE;
89:
90:
91:
92:
93: public static $showBar = TRUE;
94:
95:
96: private static $panels = array();
97:
98:
99: private static $dumps;
100:
101:
102: private static $errors;
103:
104:
105:
106:
107: const DEBUG = 'debug';
108: const INFO = 'info';
109: const WARNING = 'warning';
110: const ERROR = 'error';
111: const CRITICAL = 'critical';
112:
113:
114:
115:
116: 117: 118:
119: final public function __construct()
120: {
121: throw new LogicException("Cannot instantiate static class " . get_class($this));
122: }
123:
124:
125:
126: 127: 128: 129:
130: public static function _init()
131: {
132: self::$time = microtime(TRUE);
133: self::$consoleMode = PHP_SAPI === 'cli';
134: self::$productionMode = self::DETECT;
135: if (self::$consoleMode) {
136: self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . $_SERVER['argv'][0];
137: } else {
138: self::$firebugDetected = isset($_SERVER['HTTP_X_FIRELOGGER']);
139: self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
140: if (isset($_SERVER['REQUEST_URI'])) {
141: self::$source = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://')
142: . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''))
143: . $_SERVER['REQUEST_URI'];
144: }
145: }
146:
147: $tab = array(__CLASS__, 'renderTab'); $panel = array(__CLASS__, 'renderPanel');
148: self::addPanel(new DebugPanel('time', $tab, $panel));
149: self::addPanel(new DebugPanel('memory', $tab, $panel));
150: self::addPanel(new DebugPanel('errors', $tab, $panel));
151: self::addPanel(new DebugPanel('dumps', $tab, $panel));
152: }
153:
154:
155:
156:
157:
158:
159:
160: 161: 162: 163: 164: 165:
166: public static function dump($var, $return = FALSE)
167: {
168: if (!$return && self::$productionMode) {
169: return $var;
170: }
171:
172: $output = "<pre class=\"nette-dump\">" . self::_dump($var, 0) . "</pre>\n";
173:
174: if (!$return && self::$showLocation) {
175: $trace = debug_backtrace();
176: $i = isset($trace[1]['class']) && $trace[1]['class'] === __CLASS__ ? 1 : 0;
177: if (isset($trace[$i]['file'], $trace[$i]['line'])) {
178: $output = substr_replace($output, ' <small>' . htmlspecialchars("in file {$trace[$i]['file']} on line {$trace[$i]['line']}", ENT_NOQUOTES) . '</small>', -8, 0);
179: }
180: }
181:
182: if (self::$consoleMode) {
183: $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES);
184: }
185:
186: if ($return) {
187: return $output;
188:
189: } else {
190: echo $output;
191: return $var;
192: }
193: }
194:
195:
196:
197: 198: 199: 200: 201: 202:
203: public static function barDump($var, $title = NULL)
204: {
205: if (!self::$productionMode) {
206: $dump = array();
207: foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
208: $dump[$key] = self::_dump($val, 0);
209: }
210: self::$dumps[] = array('title' => $title, 'dump' => $dump);
211: }
212: return $var;
213: }
214:
215:
216:
217: 218: 219: 220: 221: 222:
223: private static function _dump(&$var, $level)
224: {
225: static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
226: if ($tableUtf === NULL) {
227: foreach (range("\x00", "\xFF") as $ch) {
228: if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
229: elseif (ord($ch) < 127) $tableUtf[$ch] = $tableBin[$ch] = $ch;
230: else { $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); }
231: }
232: $tableBin["\\"] = '\\\\';
233: $tableBin["\r"] = '\\r';
234: $tableBin["\n"] = '\\n';
235: $tableBin["\t"] = '\\t';
236: $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
237: }
238:
239: if (is_bool($var)) {
240: return ($var ? 'TRUE' : 'FALSE') . "\n";
241:
242: } elseif ($var === NULL) {
243: return "NULL\n";
244:
245: } elseif (is_int($var)) {
246: return "$var\n";
247:
248: } elseif (is_float($var)) {
249: $var = (string) $var;
250: if (strpos($var, '.') === FALSE) $var .= '.0';
251: return "$var\n";
252:
253: } elseif (is_string($var)) {
254: if (self::$maxLen && strlen($var) > self::$maxLen) {
255: $s = htmlSpecialChars(substr($var, 0, self::$maxLen), ENT_NOQUOTES) . ' ... ';
256: } else {
257: $s = htmlSpecialChars($var, ENT_NOQUOTES);
258: }
259: $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
260: $len = strlen($var);
261: return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n";
262:
263: } elseif (is_array($var)) {
264: $s = "<span>array</span>(" . count($var) . ") ";
265: $space = str_repeat($space1 = ' ', $level);
266: $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
267:
268: static $marker;
269: if ($marker === NULL) $marker = uniqid("\x00", TRUE);
270: if (empty($var)) {
271:
272: } elseif (isset($var[$marker])) {
273: $brackets = $var[$marker];
274: $s .= "$brackets[0] *RECURSION* $brackets[1]";
275:
276: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
277: $s .= "<code>$brackets[0]\n";
278: $var[$marker] = $brackets;
279: foreach ($var as $k => &$v) {
280: if ($k === $marker) continue;
281: $k = is_int($k) ? $k : '"' . strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf) . '"';
282: $s .= "$space$space1$k => " . self::_dump($v, $level + 1);
283: }
284: unset($var[$marker]);
285: $s .= "$space$brackets[1]</code>";
286:
287: } else {
288: $s .= "$brackets[0] ... $brackets[1]";
289: }
290: return $s . "\n";
291:
292: } elseif (is_object($var)) {
293: $arr = (array) $var;
294: $s = "<span>" . get_class($var) . "</span>(" . count($arr) . ") ";
295: $space = str_repeat($space1 = ' ', $level);
296:
297: static $list = array();
298: if (empty($arr)) {
299:
300: } elseif (in_array($var, $list, TRUE)) {
301: $s .= "{ *RECURSION* }";
302:
303: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
304: $s .= "<code>{\n";
305: $list[] = $var;
306: foreach ($arr as $k => &$v) {
307: $m = '';
308: if ($k[0] === "\x00") {
309: $m = $k[1] === '*' ? ' <span>protected</span>' : ' <span>private</span>';
310: $k = substr($k, strrpos($k, "\x00") + 1);
311: }
312: $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
313: $s .= "$space$space1\"$k\"$m => " . self::_dump($v, $level + 1);
314: }
315: array_pop($list);
316: $s .= "$space}</code>";
317:
318: } else {
319: $s .= "{ ... }";
320: }
321: return $s . "\n";
322:
323: } elseif (is_resource($var)) {
324: return "<span>" . get_resource_type($var) . " resource</span>\n";
325:
326: } else {
327: return "<span>unknown type</span>\n";
328: }
329: }
330:
331:
332:
333: 334: 335: 336: 337:
338: public static function timer($name = NULL)
339: {
340: static $time = array();
341: $now = microtime(TRUE);
342: $delta = isset($time[$name]) ? $now - $time[$name] : 0;
343: $time[$name] = $now;
344: return $delta;
345: }
346:
347:
348:
349:
350:
351:
352:
353: 354: 355: 356: 357: 358: 359:
360: public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL)
361: {
362: error_reporting(E_ALL | E_STRICT);
363:
364: 365: if (is_bool($mode)) {
366: self::$productionMode = $mode;
367:
368: } elseif (is_string($mode)) { 369: $mode = preg_split('#[,\s]+#', $mode);
370: }
371:
372: if (is_array($mode)) { 373: self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE);
374: }
375:
376: if (self::$productionMode === self::DETECT) {
377: if (class_exists('Environment')) {
378: self::$productionMode = Environment::isProduction();
379:
380: } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) { 381: $addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
382: $oct = explode('.', $addr);
383: self::$productionMode = $addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
384: && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')));
385:
386: } else {
387: self::$productionMode = !self::$consoleMode;
388: }
389: }
390:
391: 392: if (self::$productionMode) {
393: if (is_string($logDirectory) || $logDirectory === FALSE) {
394: self::$logDirectory = $logDirectory;
395: } else {
396: self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log/' : getcwd() . '/log';
397: }
398: if (self::$logDirectory) {
399: ini_set('error_log', self::$logDirectory . '/php_error.log');
400: }
401: } else {
402: self::$logDirectory = FALSE;
403: }
404:
405: 406: if (function_exists('ini_set')) {
407: ini_set('display_errors', !self::$productionMode); 408: ini_set('html_errors', FALSE);
409: ini_set('log_errors', FALSE);
410:
411: } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { 412: throw new NotSupportedException('Function ini_set() must be enabled.');
413: }
414:
415: if ($email) {
416: if (!is_string($email)) {
417: throw new InvalidArgumentException('E-mail address must be a string.');
418: }
419: self::$email = $email;
420: }
421:
422: if (!defined('E_DEPRECATED')) {
423: define('E_DEPRECATED', 8192);
424: }
425:
426: if (!defined('E_USER_DEPRECATED')) {
427: define('E_USER_DEPRECATED', 16384);
428: }
429:
430: if (!self::$enabled) {
431: register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
432: set_exception_handler(array(__CLASS__, '_exceptionHandler'));
433: set_error_handler(array(__CLASS__, '_errorHandler'));
434: self::$enabled = TRUE;
435: }
436: }
437:
438:
439:
440: 441: 442: 443:
444: public static function isEnabled()
445: {
446: return self::$enabled;
447: }
448:
449:
450:
451: 452: 453: 454: 455: 456:
457: public static function log($message, $priority = self::INFO)
458: {
459: if (self::$logDirectory === FALSE) {
460: return;
461:
462: } elseif (!self::$logDirectory) {
463: throw new InvalidStateException('Logging directory is not specified in Debug::$logDirectory.');
464:
465: } elseif (!is_dir(self::$logDirectory)) {
466: throw new DirectoryNotFoundException("Directory '" . self::$logDirectory . "' is not found or is not directory.");
467: }
468:
469: if ($message instanceof Exception) {
470: $exception = $message;
471: $message = "PHP Fatal error: "
472: . ($message instanceof FatalErrorException ? $exception->getMessage() : "Uncaught exception " . get_class($exception) . " with message '" . $exception->getMessage() . "'")
473: . " in " . $exception->getFile() . ":" . $exception->getLine();
474: }
475:
476: error_log(@date('[Y-m-d H-i-s] ') . trim($message) . (self::$source ? ' @ ' . self::$source : '') . PHP_EOL, 3, self::$logDirectory . '/' . strtolower($priority) . '.log');
477:
478: if (($priority === self::ERROR || $priority === self::CRITICAL) && self::$email
479: && @filemtime(self::$logDirectory . '/email-sent') + self::$emailSnooze < time() 480: && @file_put_contents(self::$logDirectory . '/email-sent', 'sent')) { 481: call_user_func(self::$mailer, $message);
482: }
483:
484: if (isset($exception)) {
485: $hash = md5($exception . (method_exists($exception, 'getPrevious') ? $exception->getPrevious() : (isset($exception->previous) ? $exception->previous : '')));
486: foreach (new DirectoryIterator(self::$logDirectory) as $entry) {
487: if (strpos($entry, $hash)) {
488: $skip = TRUE; break;
489: }
490: }
491: if (empty($skip) && $logHandle = @fopen(self::$logDirectory . "/exception " . @date('Y-m-d H-i-s') . " $hash.html", 'w')) {
492: ob_start(); 493: ob_start(create_function('$buffer', 'extract(NClosureFix::$vars['.NClosureFix::uses(array('logHandle'=>$logHandle)).'], EXTR_REFS); fwrite($logHandle, $buffer); '), 1);
494: self::paintBlueScreen($exception);
495: ob_end_flush();
496: ob_end_clean();
497: fclose($logHandle);
498: }
499: }
500: }
501:
502:
503:
504: 505: 506: 507: 508:
509: public static function _shutdownHandler()
510: {
511: 512: static $types = array(
513: E_ERROR => 1,
514: E_CORE_ERROR => 1,
515: E_COMPILE_ERROR => 1,
516: E_PARSE => 1,
517: );
518: $error = error_get_last();
519: if (isset($types[$error['type']])) {
520: self::_exceptionHandler(new FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL));
521: return;
522: }
523:
524: 525: if (self::$showBar && !self::$productionMode && !self::$ajaxDetected && !self::$consoleMode
526: && (!preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())))) {
527: self::paintDebugBar();
528: }
529: }
530:
531:
532:
533: 534: 535: 536: 537: 538:
539: public static function _exceptionHandler(Exception $exception)
540: {
541: if (!headers_sent()) { 542: header('HTTP/1.1 500 Internal Server Error');
543: }
544:
545: try {
546: self::log($exception, self::ERROR);
547: } catch (Exception $e) {
548: echo 'Debug fatal error: ', get_class($e), ': ', ($e->getCode() ? '#' . $e->getCode() . ' ' : '') . $e->getMessage(), "\n";
549: exit;
550: }
551:
552: $htmlMode = !self::$ajaxDetected && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()));
553:
554: if (self::$productionMode) {
555: if (self::$consoleMode) {
556: echo "ERROR: the server encountered an internal error and was unable to complete your request.\n";
557:
558: } elseif ($htmlMode) {
559: echo "<!DOCTYPE html><meta name=robots content=noindex><meta name=generator content='Nette Framework'>\n\n";
560: echo "<style>body{color:#333;background:white;width:500px;margin:100px auto}h1{font:bold 47px/1.5 sans-serif;margin:.6em 0}p{font:21px/1.5 Georgia,serif;margin:1.5em 0}small{font-size:70%;color:gray}</style>\n\n";
561: echo "<title>Server Error</title>\n\n<h1>Server Error</h1>\n\n<p>We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.</p>\n\n<p><small>error 500</small></p>";
562: }
563:
564: } else {
565: if (self::$consoleMode) { 566: echo "$exception\n";
567:
568: } elseif (self::$firebugDetected && !headers_sent() && !$htmlMode) { 569: self::fireLog($exception, self::ERROR);
570:
571: } elseif ($htmlMode) { 572: self::paintBlueScreen($exception);
573: }
574: }
575:
576: foreach (self::$onFatalError as $handler) {
577: call_user_func($handler, $exception);
578: }
579: }
580:
581:
582:
583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593:
594: public static function _errorHandler($severity, $message, $file, $line, $context)
595: {
596: if (self::$scream) {
597: error_reporting(E_ALL | E_STRICT);
598: }
599:
600: if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
601: throw new FatalErrorException($message, 0, $severity, $file, $line, $context);
602:
603: } elseif (($severity & error_reporting()) !== $severity) {
604: return FALSE; 605:
606: } elseif (self::$strictMode && !self::$productionMode) {
607: self::_exceptionHandler(new FatalErrorException($message, 0, $severity, $file, $line, $context));
608: exit;
609: }
610:
611: static $types = array(
612: E_WARNING => 'Warning',
613: E_COMPILE_WARNING => 'Warning', 614: E_USER_WARNING => 'Warning',
615: E_NOTICE => 'Notice',
616: E_USER_NOTICE => 'Notice',
617: E_STRICT => 'Strict standards',
618: E_DEPRECATED => 'Deprecated',
619: E_USER_DEPRECATED => 'Deprecated',
620: );
621:
622: $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message";
623:
624: self::log("$message in $file:$line", self::ERROR); 625:
626: if (self::$productionMode) {
627: return NULL;
628:
629: } else {
630: if (self::$showBar) {
631: self::$errors[] = "$message in " . (self::$editor ? '<a href="' . htmlspecialchars(strtr(self::$editor, array('%file' => rawurlencode($file), '%line' => $line))) . "\">$file:$line</a>" : "$file:$line");
632: }
633: if (self::$firebugDetected && !headers_sent()) {
634: self::fireLog(new ErrorException($message, 0, $severity, $file, $line), self::WARNING);
635: }
636: return self::$consoleMode || (!self::$showBar && !self::$ajaxDetected) ? FALSE : NULL;
637: }
638:
639: return FALSE; 640: }
641:
642:
643:
644:
645: public static function processException(Exception $exception)
646: {
647: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::log($exception, Debug::ERROR) instead.', E_USER_WARNING);
648: self::log($exception, self::ERROR);
649: }
650:
651:
652:
653: 654: 655: 656: 657:
658: public static function toStringException(Exception $exception)
659: {
660: if (self::$enabled) {
661: self::_exceptionHandler($exception);
662: } else {
663: trigger_error($exception->getMessage(), E_USER_ERROR);
664: }
665: exit;
666: }
667:
668:
669:
670: 671: 672: 673: 674: 675:
676: public static function paintBlueScreen(Exception $exception)
677: {
678: if (class_exists('Environment', FALSE)) {
679: $application = Environment::getContext()->hasService('Nette\\Application\\Application', TRUE) ? Environment::getContext()->getService('Nette\\Application\\Application') : NULL;
680: }
681:
682: require dirname(__FILE__) . '/templates/bluescreen.phtml';
683: }
684:
685:
686:
687: 688: 689: 690: 691:
692: public static function paintDebugBar()
693: {
694: $panels = array();
695: foreach (self::$panels as $panel) {
696: $panels[] = array(
697: 'id' => preg_replace('#[^a-z0-9]+#i', '-', $panel->getId()),
698: 'tab' => $tab = (string) $panel->getTab(),
699: 'panel' => $tab ? (string) $panel->getPanel() : NULL,
700: );
701: }
702: require dirname(__FILE__) . '/templates/bar.phtml';
703: }
704:
705:
706:
707: 708: 709: 710:
711: public static function tryError()
712: {
713: error_reporting(0);
714: $old = self::$scream;
715: self::$scream = FALSE;
716: trigger_error(''); 717: self::$scream = $old;
718: }
719:
720:
721:
722: 723: 724: 725: 726:
727: public static function catchError(& $message)
728: {
729: error_reporting(E_ALL | E_STRICT);
730: $error = error_get_last();
731: if ($error && $error['message'] !== '') {
732: $message = $error['message'];
733: return TRUE;
734: } else {
735: $message = NULL;
736: return FALSE;
737: }
738: }
739:
740:
741:
742: 743: 744: 745: 746:
747: private static function defaultMailer($message)
748: {
749: $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] :
750: (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '');
751:
752: $parts = str_replace(
753: array("\r\n", "\n"),
754: array("\n", PHP_EOL),
755: array(
756: 'headers' => "From: noreply@$host\nX-Mailer: Nette Framework\n",
757: 'subject' => "PHP: An error occurred on the server $host",
758: 'body' => "[" . @date('Y-m-d H:i:s') . "] $message", 759: )
760: );
761:
762: mail(self::$email, $parts['subject'], $parts['body'], $parts['headers']);
763: }
764:
765:
766:
767:
768:
769:
770:
771: 772: 773: 774: 775:
776: public static function addPanel(IDebugPanel $panel)
777: {
778: self::$panels[] = $panel;
779: }
780:
781:
782:
783: 784: 785: 786: 787: 788:
789: public static function renderTab($id)
790: {
791: switch ($id) {
792: case 'time':
793: require dirname(__FILE__) . '/templates/bar.time.tab.phtml';
794: return;
795: case 'memory':
796: require dirname(__FILE__) . '/templates/bar.memory.tab.phtml';
797: return;
798: case 'dumps':
799: if (!Debug::$dumps) return;
800: require dirname(__FILE__) . '/templates/bar.dumps.tab.phtml';
801: return;
802: case 'errors':
803: if (!Debug::$errors) return;
804: require dirname(__FILE__) . '/templates/bar.errors.tab.phtml';
805: }
806: }
807:
808:
809:
810: 811: 812: 813: 814: 815:
816: public static function renderPanel($id)
817: {
818: switch ($id) {
819: case 'dumps':
820: require dirname(__FILE__) . '/templates/bar.dumps.panel.phtml';
821: return;
822: case 'errors':
823: require dirname(__FILE__) . '/templates/bar.errors.panel.phtml';
824: }
825: }
826:
827:
828:
829:
830:
831:
832:
833: 834: 835: 836: 837: 838:
839: public static function fireLog($message)
840: {
841: if (self::$productionMode || headers_sent()) return FALSE;
842:
843: static $payload = array('logs' => array());
844:
845: $item = array(
846: 'name' => 'PHP',
847: 'level' => 'debug',
848: 'order' => count($payload['logs']),
849: 'time' => str_pad(number_format((microtime(TRUE) - self::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms',
850: 'template' => '',
851: 'message' => '',
852: 'style' => 'background:#767ab6',
853: );
854:
855: $args = func_get_args();
856: if (isset($args[0]) && is_string($args[0])) {
857: $item['template'] = array_shift($args);
858: }
859:
860: if (isset($args[0]) && $args[0] instanceof Exception) {
861: $e = array_shift($args);
862: $trace = $e->getTrace();
863: if (isset($trace[0]['class']) && $trace[0]['class'] === __CLASS__ && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler')) {
864: unset($trace[0]);
865: }
866:
867: $item['exc_info'] = array(
868: $e->getMessage(),
869: $e->getFile(),
870: array(),
871: );
872: $item['exc_frames'] = array();
873:
874: foreach ($trace as $frame) {
875: $frame += array('file' => null, 'line' => null, 'class' => null, 'type' => null, 'function' => null, 'object' => null, 'args' => null);
876: $item['exc_info'][2][] = array($frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']);
877: $item['exc_frames'][] = $frame['args'];
878: };
879:
880: $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile());
881: $item['template'] = ($e instanceof ErrorException ? '' : get_class($e) . ': ') . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine();
882: array_unshift($trace, array('file' => $e->getFile(), 'line' => $e->getLine()));
883:
884: } else {
885: $trace = debug_backtrace();
886: if (isset($trace[0]['class']) && $trace[0]['class'] === __CLASS__ && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler')) {
887: unset($trace[0]);
888: }
889: }
890:
891: if (isset($args[0]) && in_array($args[0], array(self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL), TRUE)) {
892: $item['level'] = array_shift($args);
893: }
894:
895: $item['args'] = $args;
896:
897: foreach ($trace as $frame) {
898: if (isset($frame['file']) && is_file($frame['file'])) {
899: $item['pathname'] = $frame['file'];
900: $item['lineno'] = $frame['line'];
901: break;
902: }
903: }
904:
905: $payload['logs'][] = $item;
906: foreach (str_split(base64_encode(@json_encode($payload)), 4990) as $k => $v) {
907: header("FireLogger-de11e-$k:$v");
908: }
909: return TRUE;
910: }
911:
912:
913:
914: 915: 916: 917: 918: 919:
920: private static function fireDump(&$var, $level = 0)
921: {
922: if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) {
923: return $var;
924:
925: } elseif (is_string($var)) {
926: if (self::$maxLen && strlen($var) > self::$maxLen) {
927: $var = substr($var, 0, self::$maxLen) . " \xE2\x80\xA6 ";
928: }
929: return @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $var)); 930:
931: } elseif (is_array($var)) {
932: static $marker;
933: if ($marker === NULL) $marker = uniqid("\x00", TRUE);
934: if (isset($var[$marker])) {
935: return "\xE2\x80\xA6RECURSION\xE2\x80\xA6";
936:
937: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
938: $var[$marker] = TRUE;
939: $res = array();
940: foreach ($var as $k => &$v) {
941: if ($k !== $marker) $res[self::fireDump($k)] = self::fireDump($v, $level + 1);
942: }
943: unset($var[$marker]);
944: return $res;
945:
946: } else {
947: return " \xE2\x80\xA6 ";
948: }
949:
950: } elseif (is_object($var)) {
951: $arr = (array) $var;
952: static $list = array();
953: if (in_array($var, $list, TRUE)) {
954: return "\xE2\x80\xA6RECURSION\xE2\x80\xA6";
955:
956: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
957: $list[] = $var;
958: $res = array(" \xC2\xBBclass\xC2\xAB" => get_class($var));
959: foreach ($arr as $k => &$v) {
960: if ($k[0] === "\x00") {
961: $k = substr($k, strrpos($k, "\x00") + 1);
962: }
963: $res[self::fireDump($k)] = self::fireDump($v, $level + 1);
964: }
965: array_pop($list);
966: return $res;
967:
968: } else {
969: return " \xE2\x80\xA6 ";
970: }
971:
972: } elseif (is_resource($var)) {
973: return "resource " . get_resource_type($var);
974:
975: } else {
976: return "unknown type";
977: }
978: }
979:
980: }
981:
982:
983:
984: Debug::_init();
985: