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