1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Tracy;
9:
10: use Tracy;
11:
12:
13: 14: 15: 16: 17:
18: class BlueScreen
19: {
20:
21: public $info = array();
22:
23:
24: private $panels = array();
25:
26:
27: public $collapsePaths = array();
28:
29:
30: public function __construct()
31: {
32: $this->collapsePaths[] = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy$#', strtr(__DIR__, '\\', '/'), $m)
33: ? $m[1]
34: : __DIR__;
35: }
36:
37:
38: 39: 40: 41: 42:
43: public function addPanel($panel)
44: {
45: if (!in_array($panel, $this->panels, TRUE)) {
46: $this->panels[] = $panel;
47: }
48: return $this;
49: }
50:
51:
52: 53: 54: 55: 56:
57: public function render(\Exception $exception)
58: {
59: $panels = $this->panels;
60: $info = array_filter($this->info);
61: $source = Helpers::getSource();
62: $sourceIsUrl = preg_match('#^https?://#', $source);
63: $title = $exception instanceof \ErrorException
64: ? Helpers::errorTypeToString($exception->getSeverity())
65: : get_class($exception);
66: $skipError = $sourceIsUrl && $exception instanceof \ErrorException && !empty($exception->skippable)
67: ? $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error'
68: : NULL;
69:
70: require __DIR__ . '/assets/BlueScreen/bluescreen.phtml';
71: }
72:
73:
74: 75: 76: 77: 78: 79: 80:
81: public static function highlightFile($file, $line, $lines = 15, array $vars = NULL)
82: {
83: $source = @file_get_contents($file);
84: if ($source) {
85: $source = static::highlightPhp($source, $line, $lines, $vars);
86: if ($editor = Helpers::editorUri($file, $line)) {
87: $source = substr_replace($source, ' data-tracy-href="' . htmlspecialchars($editor, ENT_QUOTES, 'UTF-8') . '"', 4, 0);
88: }
89: return $source;
90: }
91: }
92:
93:
94: 95: 96: 97: 98: 99: 100:
101: public static function highlightPhp($source, $line, $lines = 15, array $vars = NULL)
102: {
103: if (function_exists('ini_set')) {
104: ini_set('highlight.comment', '#998; font-style: italic');
105: ini_set('highlight.default', '#000');
106: ini_set('highlight.html', '#06B');
107: ini_set('highlight.keyword', '#D24; font-weight: bold');
108: ini_set('highlight.string', '#080');
109: }
110:
111: $source = str_replace(array("\r\n", "\r"), "\n", $source);
112: $source = explode("\n", highlight_string($source, TRUE));
113: $out = $source[0];
114: $source = str_replace('<br />', "\n", $source[1]);
115: $out .= static::highlightLine($source, $line, $lines);
116:
117: if ($vars) {
118: $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function($m) use ($vars) {
119: return array_key_exists($m[1], $vars)
120: ? '" title="'
121: . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], array(Dumper::DEPTH => 1)))))
122: . $m[0]
123: : $m[0];
124: }, $out);
125: }
126:
127: $out = str_replace(' ', ' ', $out);
128: return "<pre class='php'><div>$out</div></pre>";
129: }
130:
131:
132:
133: 134: 135: 136:
137: public static function highlightLine($html, $line, $lines = 15)
138: {
139: $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html));
140: $out = '';
141: $spans = 1;
142: $start = $i = max(1, $line - floor($lines * 2/3));
143: while (--$i >= 1) {
144: if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) {
145: if ($m[1] !== '</span>') {
146: $spans++;
147: $out .= $m[1];
148: }
149: break;
150: }
151: }
152:
153: $source = array_slice($source, $start, $lines, TRUE);
154: end($source);
155: $numWidth = strlen((string) key($source));
156:
157: foreach ($source as $n => $s) {
158: $spans += substr_count($s, '<span') - substr_count($s, '</span');
159: $s = str_replace(array("\r", "\n"), array('', ''), $s);
160: preg_match_all('#<[^>]+>#', $s, $tags);
161: if ($n == $line) {
162: $out .= sprintf(
163: "<span class='highlight'>%{$numWidth}s: %s\n</span>%s",
164: $n,
165: strip_tags($s),
166: implode('', $tags[0])
167: );
168: } else {
169: $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s);
170: }
171: }
172: $out .= str_repeat('</span>', $spans) . '</code>';
173: return $out;
174: }
175:
176:
177: 178: 179: 180: 181:
182: public function isCollapsed($file)
183: {
184: foreach ($this->collapsePaths as $path) {
185: if (strpos(strtr($file, '\\', '/'), strtr("$path/", '\\', '/')) === 0) {
186: return TRUE;
187: }
188: }
189: return FALSE;
190: }
191:
192: }
193: