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