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: 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: private static $consoleData;
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 $onFatalError = array();
68:
69:
70: public static $mailer = array(__CLASS__, 'defaultMailer');
71:
72:
73: public static $emailSnooze = 172800;
74:
75:
76: private static $enabled = FALSE;
77:
78:
79: private static $logFile;
80:
81:
82: private static $logHandle;
83:
84:
85: private static $sendEmails;
86:
87:
88: private static $emailHeaders = array(
89: 'To' => '',
90: 'From' => 'noreply@%host%',
91: 'X-Mailer' => 'Nette Framework',
92: 'Subject' => 'PHP: An error occurred on the server %host%',
93: 'Body' => '[%date%] %message%',
94: );
95:
96:
97: private static $colophons = array(array(__CLASS__, 'getDefaultColophons'));
98:
99:
100:
101:
102: private static $enabledProfiler = FALSE;
103:
104:
105: public static $counters = array();
106:
107:
108:
109:
110: const LOG = 'LOG';
111: const INFO = 'INFO';
112: const WARN = 'WARN';
113: const ERROR = 'ERROR';
114: const TRACE = 'TRACE';
115: const EXCEPTION = 'EXCEPTION';
116: const GROUP_START = 'GROUP_START';
117: const GROUP_END = 'GROUP_END';
118:
119:
120:
121:
122: 123: 124:
125: final public function __construct()
126: {
127: throw new \LogicException("Cannot instantiate static class " . get_class($this));
128: }
129:
130:
131:
132: 133: 134: 135:
136: public static function _init()
137: {
138: self::$time = microtime(TRUE);
139: self::$consoleMode = PHP_SAPI === 'cli';
140: self::$productionMode = self::DETECT;
141: self::$firebugDetected = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
142: self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
143: register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
144: }
145:
146:
147:
148: 149: 150: 151: 152:
153: public static function _shutdownHandler()
154: {
155:
156: static $types = array(
157: E_ERROR => 1,
158: E_CORE_ERROR => 1,
159: E_COMPILE_ERROR => 1,
160: E_PARSE => 1,
161: );
162:
163: $error = error_get_last();
164: if (self::$enabled && isset($types[$error['type']])) {
165: if (!headers_sent()) {
166: header('HTTP/1.1 500 Internal Server Error');
167: }
168:
169: if (ini_get('html_errors')) {
170: $error['message'] = html_entity_decode(strip_tags($error['message']), ENT_QUOTES, 'UTF-8');
171: }
172:
173: self::processException(new \FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL), TRUE);
174: }
175:
176:
177:
178: if (self::$productionMode) {
179: return;
180: }
181: foreach (headers_list() as $header) {
182: if (strncasecmp($header, 'Content-Type:', 13) === 0) {
183: if (substr($header, 14, 9) === 'text/html') {
184: break;
185: }
186: return;
187: }
188: }
189:
190:
191: if (self::$enabledProfiler) {
192: if (self::$firebugDetected) {
193: self::fireLog('Nette profiler', self::GROUP_START);
194: foreach (self::$colophons as $callback) {
195: foreach ((array) call_user_func($callback, 'profiler') as $line) self::fireLog(strip_tags($line));
196: }
197: self::fireLog(NULL, self::GROUP_END);
198: }
199:
200: if (!self::$ajaxDetected) {
201: $colophons = self::$colophons;
202: require __DIR__ . '/templates/profiler.phtml';
203: }
204: }
205:
206:
207:
208: if (self::$consoleData) {
209: $payload = self::$consoleData;
210: require __DIR__ . '/templates/console.phtml';
211: }
212: }
213:
214:
215:
216:
217:
218:
219:
220: 221: 222: 223: 224: 225:
226: public static function dump($var, $return = FALSE)
227: {
228: if (!$return && self::$productionMode) {
229: return $var;
230: }
231:
232: $output = "<pre class=\"dump\">" . self::_dump($var, 0) . "</pre>\n";
233:
234: if (!$return && self::$showLocation) {
235: $trace = debug_backtrace();
236: $i = isset($trace[1]['class']) && $trace[1]['class'] === __CLASS__ ? 1 : 0;
237: if (isset($trace[$i]['file'], $trace[$i]['line'])) {
238: $output = substr_replace($output, ' <small>' . htmlspecialchars("in file {$trace[$i]['file']} on line {$trace[$i]['line']}", ENT_NOQUOTES) . '</small>', -8, 0);
239: }
240: }
241:
242: if (self::$consoleMode) {
243: $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES);
244: }
245:
246: if ($return) {
247: return $output;
248:
249: } else {
250: echo $output;
251: return $var;
252: }
253: }
254:
255:
256:
257: 258: 259: 260: 261: 262:
263: public static function consoleDump($var, $title = NULL)
264: {
265: if (!self::$productionMode) {
266: $dump = array();
267: foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
268: $dump[$key] = self::dump($val, TRUE);
269: }
270: self::$consoleData[] = array('title' => $title, 'dump' => $dump);
271: }
272: return $var;
273: }
274:
275:
276:
277: 278: 279: 280: 281: 282:
283: private static function _dump(&$var, $level)
284: {
285: static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
286: if ($tableUtf === NULL) {
287: foreach (range("\x00", "\xFF") as $ch) {
288: 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);
289: elseif (ord($ch) < 127) $tableUtf[$ch] = $tableBin[$ch] = $ch;
290: else { $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); }
291: }
292: $tableBin["\\"] = '\\\\';
293: $tableBin["\r"] = '\\r';
294: $tableBin["\n"] = '\\n';
295: $tableBin["\t"] = '\\t';
296: $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
297: }
298:
299: if (is_bool($var)) {
300: return ($var ? 'TRUE' : 'FALSE') . "\n";
301:
302: } elseif ($var === NULL) {
303: return "NULL\n";
304:
305: } elseif (is_int($var)) {
306: return "$var\n";
307:
308: } elseif (is_float($var)) {
309: $var = (string) $var;
310: if (strpos($var, '.') === FALSE) $var .= '.0';
311: return "$var\n";
312:
313: } elseif (is_string($var)) {
314: if (self::$maxLen && strlen($var) > self::$maxLen) {
315: $s = htmlSpecialChars(substr($var, 0, self::$maxLen), ENT_NOQUOTES) . ' ... ';
316: } else {
317: $s = htmlSpecialChars($var, ENT_NOQUOTES);
318: }
319: $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
320: $len = strlen($var);
321: return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n";
322:
323: } elseif (is_array($var)) {
324: $s = "<span>array</span>(" . count($var) . ") ";
325: $space = str_repeat($space1 = ' ', $level);
326: $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
327:
328: static $marker;
329: if ($marker === NULL) $marker = uniqid("\x00", TRUE);
330: if (empty($var)) {
331:
332: } elseif (isset($var[$marker])) {
333: $brackets = $var[$marker];
334: $s .= "$brackets[0] *RECURSION* $brackets[1]";
335:
336: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
337: $s .= "<code>$brackets[0]\n";
338: $var[$marker] = $brackets;
339: foreach ($var as $k => &$v) {
340: if ($k === $marker) continue;
341: $k = is_int($k) ? $k : '"' . strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf) . '"';
342: $s .= "$space$space1$k => " . self::_dump($v, $level + 1);
343: }
344: unset($var[$marker]);
345: $s .= "$space$brackets[1]</code>";
346:
347: } else {
348: $s .= "$brackets[0] ... $brackets[1]";
349: }
350: return $s . "\n";
351:
352: } elseif (is_object($var)) {
353: $arr = (array) $var;
354: $s = "<span>" . get_class($var) . "</span>(" . count($arr) . ") ";
355: $space = str_repeat($space1 = ' ', $level);
356:
357: static $list = array();
358: if (empty($arr)) {
359:
360: } elseif (in_array($var, $list, TRUE)) {
361: $s .= "{ *RECURSION* }";
362:
363: } elseif ($level < self::$maxDepth || !self::$maxDepth) {
364: $s .= "<code>{\n";
365: $list[] = $var;
366: foreach ($arr as $k => &$v) {
367: $m = '';
368: if ($k[0] === "\x00") {
369: $m = $k[1] === '*' ? ' <span>protected</span>' : ' <span>private</span>';
370: $k = substr($k, strrpos($k, "\x00") + 1);
371: }
372: $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
373: $s .= "$space$space1\"$k\"$m => " . self::_dump($v, $level + 1);
374: }
375: array_pop($list);
376: $s .= "$space}</code>";
377:
378: } else {
379: $s .= "{ ... }";
380: }
381: return $s . "\n";
382:
383: } elseif (is_resource($var)) {
384: return "<span>" . get_resource_type($var) . " resource</span>\n";
385:
386: } else {
387: return "<span>unknown type</span>\n";
388: }
389: }
390:
391:
392:
393: 394: 395: 396: 397:
398: public static function timer($name = NULL)
399: {
400: static $time = array();
401: $now = microtime(TRUE);
402: $delta = isset($time[$name]) ? $now - $time[$name] : 0;
403: $time[$name] = $now;
404: return $delta;
405: }
406:
407:
408:
409:
410:
411:
412:
413: 414: 415: 416: 417: 418: 419:
420: public static function enable($mode = NULL, $logFile = NULL, $email = NULL)
421: {
422: error_reporting(E_ALL | E_STRICT);
423:
424:
425: if (is_bool($mode)) {
426: self::$productionMode = $mode;
427:
428: } elseif (is_string($mode)) {
429: $mode = preg_split('#[,\s]+#', $mode);
430: }
431:
432: if (is_array($mode)) {
433: self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE);
434: }
435:
436: if (self::$productionMode === self::DETECT) {
437: if (class_exists('Nette\Environment')) {
438: self::$productionMode = Environment::isProduction();
439:
440: } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) {
441: $addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
442: $oct = explode('.', $addr);
443: self::$productionMode = $addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
444: && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')));
445:
446: } else {
447: self::$productionMode = !self::$consoleMode;
448: }
449: }
450:
451:
452: if (self::$productionMode && $logFile !== FALSE) {
453: self::$logFile = 'log/php_error.log';
454:
455: if (class_exists('Nette\Environment')) {
456: if (is_string($logFile)) {
457: self::$logFile = Environment::expand($logFile);
458:
459: } else try {
460: self::$logFile = Environment::expand('%logDir%/php_error.log');
461:
462: } catch (\InvalidStateException $e) {
463: }
464:
465: } elseif (is_string($logFile)) {
466: self::$logFile = $logFile;
467: }
468:
469: ini_set('error_log', self::$logFile);
470: }
471:
472:
473: if (function_exists('ini_set')) {
474: ini_set('display_errors', !self::$productionMode);
475: ini_set('html_errors', !self::$logFile && !self::$consoleMode);
476: ini_set('log_errors', (bool) self::$logFile);
477:
478: } elseif (ini_get('log_errors') != (bool) self::$logFile ||
479: (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout'))) {
480: throw new \LogicException('Function ini_set() must be enabled.');
481: }
482:
483: self::$sendEmails = self::$logFile && $email;
484: if (self::$sendEmails) {
485: if (is_string($email)) {
486: self::$emailHeaders['To'] = $email;
487:
488: } elseif (is_array($email)) {
489: self::$emailHeaders = $email + self::$emailHeaders;
490: }
491: }
492:
493: if (!defined('E_DEPRECATED')) {
494: define('E_DEPRECATED', 8192);
495: }
496:
497: if (!defined('E_USER_DEPRECATED')) {
498: define('E_USER_DEPRECATED', 16384);
499: }
500:
501: set_exception_handler(array(__CLASS__, '_exceptionHandler'));
502: set_error_handler(array(__CLASS__, '_errorHandler'));
503: self::$enabled = TRUE;
504: }
505:
506:
507:
508: 509: 510: 511:
512: public static function isEnabled()
513: {
514: return self::$enabled;
515: }
516:
517:
518:
519: 520: 521: 522: 523: 524:
525: public static function _exceptionHandler(\Exception $exception)
526: {
527: if (!headers_sent()) {
528: header('HTTP/1.1 500 Internal Server Error');
529: }
530: self::processException($exception, TRUE);
531: exit;
532: }
533:
534:
535:
536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546:
547: public static function _errorHandler($severity, $message, $file, $line, $context)
548: {
549: if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
550: throw new \FatalErrorException($message, 0, $severity, $file, $line, $context);
551:
552: } elseif (($severity & error_reporting()) !== $severity) {
553: return NULL;
554:
555: } elseif (self::$strictMode) {
556: self::_exceptionHandler(new \FatalErrorException($message, 0, $severity, $file, $line, $context), TRUE);
557: }
558:
559: static $types = array(
560: E_WARNING => 'Warning',
561: E_USER_WARNING => 'Warning',
562: E_NOTICE => 'Notice',
563: E_USER_NOTICE => 'Notice',
564: E_STRICT => 'Strict standards',
565: E_DEPRECATED => 'Deprecated',
566: E_USER_DEPRECATED => 'Deprecated',
567: );
568:
569: $type = isset($types[$severity]) ? $types[$severity] : 'Unknown error';
570:
571: if (self::$logFile) {
572: if (self::$sendEmails) {
573: self::sendEmail("$type: $message in $file on line $line");
574: }
575: return FALSE;
576:
577: } elseif (!self::$productionMode && self::$firebugDetected && !headers_sent()) {
578: $message = strip_tags($message);
579: self::fireLog("$type: $message in $file on line $line", self::ERROR);
580: return NULL;
581: }
582:
583: return FALSE;
584: }
585:
586:
587:
588: 589: 590: 591: 592: 593:
594: public static function processException(\Exception $exception, $outputAllowed = FALSE)
595: {
596: if (!self::$enabled) {
597: return;
598:
599: } elseif (self::$logFile) {
600: try {
601: $hash = md5($exception );
602: error_log("PHP Fatal error: Uncaught $exception");
603: foreach (new \DirectoryIterator(dirname(self::$logFile)) as $entry) {
604: if (strpos($entry, $hash)) {
605: $skip = TRUE;
606: break;
607: }
608: }
609: $file = dirname(self::$logFile) . "/exception " . @date('Y-m-d H-i-s') . " $hash.html";
610: if (empty($skip) && self::$logHandle = @fopen($file, 'x')) {
611: ob_start();
612: ob_start(array(__CLASS__, '_writeFile'), 1);
613: self::_paintBlueScreen($exception);
614: ob_end_flush();
615: ob_end_clean();
616: fclose(self::$logHandle);
617: }
618: if (self::$sendEmails) {
619: self::sendEmail((string) $exception);
620: }
621: } catch (\Exception $e) {
622: if (!headers_sent()) {
623: header('HTTP/1.1 500 Internal Server Error');
624: }
625: echo 'Nette\Debug fatal error: ', get_class($e), ': ', ($e->getCode() ? '#' . $e->getCode() . ' ' : '') . $e->getMessage(), "\n";
626: exit;
627: }
628:
629: } elseif (self::$productionMode) {
630:
631:
632: } elseif (self::$consoleMode) {
633: if ($outputAllowed) {
634: echo "$exception\n";
635: foreach (self::$colophons as $callback) {
636: foreach ((array) call_user_func($callback, 'bluescreen') as $line) echo strip_tags($line) . "\n";
637: }
638: }
639:
640: } elseif (self::$firebugDetected && self::$ajaxDetected && !headers_sent()) {
641: self::fireLog($exception, self::EXCEPTION);
642:
643: } elseif ($outputAllowed) {
644: self::_paintBlueScreen($exception);
645:
646: } elseif (self::$firebugDetected && !headers_sent()) {
647: self::fireLog($exception, self::EXCEPTION);
648: }
649:
650: foreach (self::$onFatalError as $handler) {
651: call_user_func($handler, $exception);
652: }
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: }
670:
671:
672:
673: 674: 675: 676: 677: 678:
679: public static function _paintBlueScreen(\Exception $exception)
680: {
681: if (class_exists('Nette\Environment', FALSE)) {
682: $application = Environment::getServiceLocator()->hasService('Nette\\Application\\Application', TRUE) ? Environment::getServiceLocator()->getService('Nette\\Application\\Application') : NULL;
683: }
684:
685: $colophons = self::$colophons;
686: require __DIR__ . '/templates/bluescreen.phtml';
687: }
688:
689:
690:
691: 692: 693: 694: 695: 696:
697: public static function _writeFile($buffer)
698: {
699: fwrite(self::$logHandle, $buffer);
700: }
701:
702:
703:
704: 705: 706: 707: 708:
709: private static function sendEmail($message)
710: {
711: $monitorFile = self::$logFile . '.monitor';
712: if (@filemtime($monitorFile) + self::$emailSnooze < time()
713: && @file_put_contents($monitorFile, 'sent')) {
714: call_user_func(self::$mailer, $message);
715: }
716: }
717:
718:
719:
720: 721: 722: 723: 724:
725: private static function defaultMailer($message)
726: {
727: $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] :
728: (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '');
729:
730: $headers = str_replace(
731: array('%host%', '%date%', '%message%'),
732: array($host, @date('Y-m-d H:i:s', self::$time), $message),
733: self::$emailHeaders
734: );
735:
736: $subject = $headers['Subject'];
737: $to = $headers['To'];
738: $body = str_replace("\r\n", PHP_EOL, $headers['Body']);
739: unset($headers['Subject'], $headers['To'], $headers['Body']);
740: $header = '';
741: foreach ($headers as $key => $value) {
742: $header .= "$key: $value" . PHP_EOL;
743: }
744:
745: mail($to, $subject, $body, $header);
746: }
747:
748:
749:
750:
751:
752:
753:
754: 755: 756: 757:
758: public static function enableProfiler()
759: {
760: self::$enabledProfiler = TRUE;
761: }
762:
763:
764:
765: 766: 767: 768:
769: public static function disableProfiler()
770: {
771: self::$enabledProfiler = FALSE;
772: }
773:
774:
775:
776:
777:
778:
779:
780: 781: 782: 783: 784:
785: public static function addColophon($callback)
786: {
787: if (!is_callable($callback)) {
788: $able = is_callable($callback, TRUE, $textual);
789: throw new \InvalidArgumentException("Colophon handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
790: }
791:
792: if (!in_array($callback, self::$colophons, TRUE)) {
793: self::$colophons[] = $callback;
794: }
795: }
796:
797:
798:
799: 800: 801: 802: 803:
804: private static function getDefaultColophons($sender)
805: {
806: if ($sender === 'profiler') {
807: $arr[] = 'Elapsed time: <b>' . number_format((microtime(TRUE) - self::$time) * 1000, 1, '.', ' ') . '</b> ms | Allocated memory: <b>' . number_format(memory_get_peak_usage() / 1000, 1, '.', ' ') . '</b> kB';
808:
809: foreach ((array) self::$counters as $name => $value) {
810: if (is_array($value)) $value = implode(', ', $value);
811: $arr[] = htmlSpecialChars($name) . ' = <strong>' . htmlSpecialChars($value) . '</strong>';
812: }
813:
814: $autoloaded = class_exists('Nette\Loaders\AutoLoader', FALSE) ? Nette\Loaders\AutoLoader::$count : 0;
815: $s = '<span>' . count(get_included_files()) . '/' . $autoloaded . ' files</span>, ';
816:
817: $exclude = array('stdClass', 'Exception', 'ErrorException', 'Traversable', 'IteratorAggregate', 'Iterator', 'ArrayAccess', 'Serializable', 'Closure');
818: foreach (get_loaded_extensions() as $ext) {
819: $ref = new \ReflectionExtension($ext);
820: $exclude = array_merge($exclude, $ref->getClassNames());
821: }
822: $classes = array_diff(get_declared_classes(), $exclude);
823: $intf = array_diff(get_declared_interfaces(), $exclude);
824: $func = get_defined_functions();
825: $func = (array) @$func['user'];
826: $consts = get_defined_constants(TRUE);
827: $consts = array_keys((array) @$consts['user']);
828: foreach (array('classes', 'intf', 'func', 'consts') as $item) {
829: $s .= '<span ' . ($$item ? 'title="' . implode(", ", $$item) . '"' : '') . '>' . count($$item) . ' ' . $item . '</span>, ';
830: }
831: $arr[] = $s;
832: }
833:
834: if ($sender === 'bluescreen') {
835: $arr[] = 'Report generated at ' . @date('Y/m/d H:i:s', self::$time);
836: if (isset($_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])) {
837: $url = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') . htmlSpecialChars($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
838: $arr[] = '<a href="' . $url . '">' . $url . '</a>';
839: }
840: $arr[] = 'PHP ' . htmlSpecialChars(PHP_VERSION);
841: if (isset($_SERVER['SERVER_SOFTWARE'])) $arr[] = htmlSpecialChars($_SERVER['SERVER_SOFTWARE']);
842: if (class_exists('Nette\Framework')) $arr[] = htmlSpecialChars('Nette Framework ' . Framework::VERSION) . ' <i>(revision ' . htmlSpecialChars(Framework::REVISION) . ')</i>';
843: }
844: return $arr;
845: }
846:
847:
848:
849:
850:
851:
852:
853: 854: 855: 856: 857: 858:
859: public static function fireDump($var, $key)
860: {
861: self::fireSend('Dump/0.1', array((string) $key => $var));
862: return $var;
863: }
864:
865:
866:
867: 868: 869: 870: 871: 872: 873:
874: public static function fireLog($message, $priority = self::LOG, $label = NULL)
875: {
876: if ($message instanceof \Exception) {
877: if ($priority !== self::EXCEPTION && $priority !== self::TRACE) {
878: $priority = self::TRACE;
879: }
880: $message = array(
881: 'Class' => get_class($message),
882: 'Message' => $message->getMessage(),
883: 'File' => $message->getFile(),
884: 'Line' => $message->getLine(),
885: 'Trace' => $message->getTrace(),
886: 'Type' => '',
887: 'Function' => '',
888: );
889: foreach ($message['Trace'] as & $row) {
890: if (empty($row['file'])) $row['file'] = '?';
891: if (empty($row['line'])) $row['line'] = '?';
892: }
893: } elseif ($priority === self::GROUP_START) {
894: $label = $message;
895: $message = NULL;
896: }
897: return self::fireSend('FirebugConsole/0.1', self::replaceObjects(array(array('Type' => $priority, 'Label' => $label), $message)));
898: }
899:
900:
901:
902: 903: 904: 905: 906: 907: 908:
909: private static function fireSend($struct, $payload)
910: {
911: if (self::$productionMode) return NULL;
912:
913: if (headers_sent()) return FALSE;
914:
915: header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
916: header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
917:
918: static $structures;
919: $index = isset($structures[$struct]) ? $structures[$struct] : ($structures[$struct] = count($structures) + 1);
920: header("X-Wf-nette-Structure-$index: http://meta.firephp.org/Wildfire/Structure/FirePHP/$struct");
921:
922: $payload = json_encode($payload);
923: static $counter;
924: foreach (str_split($payload, 4990) as $s) {
925: $num = ++$counter;
926: header("X-Wf-nette-$index-1-n$num: |$s|\\");
927: }
928: header("X-Wf-nette-$index-1-n$num: |$s|");
929:
930: return TRUE;
931: }
932:
933:
934:
935: 936: 937: 938: 939:
940: static private function replaceObjects($val)
941: {
942: if (is_object($val)) {
943: return 'object ' . get_class($val) . '';
944:
945: } elseif (is_string($val)) {
946: return @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $val));
947:
948: } elseif (is_array($val)) {
949: foreach ($val as $k => $v) {
950: unset($val[$k]);
951: $k = @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $k));
952: $val[$k] = self::replaceObjects($v);
953: }
954: }
955:
956: return $val;
957: }
958:
959: }
960:
961:
962:
963: Debug::_init();
964: