1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Tracy;
9:
10:
11: 12: 13:
14: class OutputDebugger
15: {
16: const BOM = "\xEF\xBB\xBF";
17:
18:
19: private $list = [];
20:
21:
22: public static function enable()
23: {
24: $me = new static;
25: $me->start();
26: }
27:
28:
29: public function start()
30: {
31: foreach (get_included_files() as $file) {
32: if (fread(fopen($file, 'r'), 3) === self::BOM) {
33: $this->list[] = [$file, 1, self::BOM];
34: }
35: }
36: ob_start([$this, 'handler'], 1);
37: }
38:
39:
40:
41: public function handler($s, $phase)
42: {
43: $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
44: if (isset($trace[0]['file'], $trace[0]['line'])) {
45: $stack = $trace;
46: unset($stack[0]['line'], $stack[0]['args']);
47: $i = count($this->list);
48: if ($i && $this->list[$i - 1][3] === $stack) {
49: $this->list[$i - 1][2] .= $s;
50: } else {
51: $this->list[] = [$trace[0]['file'], $trace[0]['line'], $s, $stack];
52: }
53: }
54: if ($phase === PHP_OUTPUT_HANDLER_FINAL) {
55: return $this->renderHtml();
56: }
57: }
58:
59:
60: private function renderHtml()
61: {
62: $res = '<style>code, pre {white-space:nowrap} a {text-decoration:none} pre {color:gray;display:inline} big {color:red}</style><code>';
63: foreach ($this->list as $item) {
64: $stack = [];
65: foreach (array_slice($item[3], 1) as $t) {
66: $t += ['class' => '', 'type' => '', 'function' => ''];
67: $stack[] = "$t[class]$t[type]$t[function]()"
68: . (isset($t['file'], $t['line']) ? ' in ' . basename($t['file']) . ":$t[line]" : '');
69: }
70:
71: $res .= '<span title="' . Helpers::escapeHtml(implode("\n", $stack)) . '">'
72: . Helpers::editorLink($item[0], $item[1]) . ' '
73: . str_replace(self::BOM, '<big>BOM</big>', Dumper::toHtml($item[2]))
74: . "</span><br>\n";
75: }
76: return $res . '</code>';
77: }
78: }
79: