1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Tracy;
9:
10:
11: 12: 13:
14: class Bar
15: {
16:
17: private $panels = [];
18:
19:
20: private $dispatched;
21:
22:
23: 24: 25: 26: 27: 28:
29: public function addPanel(IBarPanel $panel, $id = NULL)
30: {
31: if ($id === NULL) {
32: $c = 0;
33: do {
34: $id = get_class($panel) . ($c++ ? "-$c" : '');
35: } while (isset($this->panels[$id]));
36: }
37: $this->panels[$id] = $panel;
38: return $this;
39: }
40:
41:
42: 43: 44: 45: 46:
47: public function getPanel($id)
48: {
49: return isset($this->panels[$id]) ? $this->panels[$id] : NULL;
50: }
51:
52:
53: 54: 55: 56:
57: public function render()
58: {
59: $useSession = $this->dispatched && session_status() === PHP_SESSION_ACTIVE;
60: $redirectQueue = & $_SESSION['_tracy']['redirect'];
61:
62: if (!Helpers::isHtmlMode() && !Helpers::isAjax()) {
63: return;
64:
65: } elseif (Helpers::isAjax()) {
66: $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')];
67: $dumps = Dumper::fetchLiveData();
68: $contentId = $useSession ? $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax' : NULL;
69:
70: } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) {
71: $redirectQueue = array_slice((array) $redirectQueue, -10);
72: Dumper::fetchLiveData();
73: Dumper::$livePrefix = count($redirectQueue) . 'p';
74: $redirectQueue[] = [
75: 'panels' => $this->renderPanels('-r' . count($redirectQueue)),
76: 'dumps' => Dumper::fetchLiveData(),
77: ];
78: return;
79:
80: } else {
81: $rows[] = (object) ['type' => 'main', 'panels' => $this->renderPanels()];
82: $dumps = Dumper::fetchLiveData();
83: foreach (array_reverse((array) $redirectQueue) as $info) {
84: $rows[] = (object) ['type' => 'redirect', 'panels' => $info['panels']];
85: $dumps += $info['dumps'];
86: }
87: $redirectQueue = NULL;
88: $contentId = $useSession ? substr(md5(uniqid('', TRUE)), 0, 10) : NULL;
89: }
90:
91: ob_start(function () {});
92: require __DIR__ . '/assets/Bar/panels.phtml';
93: require __DIR__ . '/assets/Bar/bar.phtml';
94: $content = Helpers::fixEncoding(ob_get_clean());
95:
96: if ($contentId) {
97: $queue = & $_SESSION['_tracy']['bar'];
98: $queue = array_slice(array_filter((array) $queue), -5, NULL, TRUE);
99: $queue[$contentId] = ['content' => $content, 'dumps' => $dumps];
100: }
101:
102: if (Helpers::isHtmlMode()) {
103: $baseUrl = extension_loaded('xdebug') ? '?XDEBUG_SESSION_STOP=1&' : '?';
104: require __DIR__ . '/assets/Bar/loader.phtml';
105: }
106: }
107:
108:
109: 110: 111:
112: private function renderPanels($suffix = NULL)
113: {
114: set_error_handler(function ($severity, $message, $file, $line) {
115: if (error_reporting() & $severity) {
116: throw new \ErrorException($message, 0, $severity, $file, $line);
117: }
118: });
119:
120: $obLevel = ob_get_level();
121: $panels = [];
122:
123: foreach ($this->panels as $id => $panel) {
124: $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix;
125: try {
126: $tab = (string) $panel->getTab();
127: $panelHtml = $tab ? (string) $panel->getPanel() : NULL;
128: if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) {
129: $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated');
130: }
131:
132: } catch (\Throwable $e) {
133: } catch (\Exception $e) {
134: }
135: if (isset($e)) {
136: while (ob_get_level() > $obLevel) {
137: ob_end_clean();
138: }
139: $idHtml = "error-$idHtml";
140: $tab = "Error in $id";
141: $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>';
142: unset($e);
143: }
144: $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml];
145: }
146:
147: restore_error_handler();
148: return $panels;
149: }
150:
151:
152: 153: 154: 155:
156: public function dispatchAssets()
157: {
158: if (isset($_GET['_tracy_bar']) && $_GET['_tracy_bar'] === 'assets') {
159: header('Content-Type: text/javascript');
160: header('Cache-Control: max-age=864000');
161: header_remove('Pragma');
162: header_remove('Set-Cookie');
163: $css = file_get_contents(__DIR__ . '/assets/Bar/bar.css')
164: . file_get_contents(__DIR__ . '/assets/Toggle/toggle.css')
165: . file_get_contents(__DIR__ . '/assets/Dumper/dumper.css')
166: . file_get_contents(__DIR__ . '/assets/BlueScreen/bluescreen.css');
167: $js = file_get_contents(__DIR__ . '/assets/Bar/bar.js')
168: . file_get_contents(__DIR__ . '/assets/Toggle/toggle.js')
169: . file_get_contents(__DIR__ . '/assets/Dumper/dumper.js')
170: . file_get_contents(__DIR__ . '/assets/BlueScreen/bluescreen.js');
171: echo 'localStorage.setItem("tracy-style", ' . json_encode($css) . ');';
172: echo 'localStorage.setItem("tracy-script", ' . json_encode($js) . ');';
173: echo 'localStorage.setItem("tracy-version", ' . json_encode(Debugger::VERSION) . ');';
174: return TRUE;
175: }
176: }
177:
178:
179: 180: 181: 182:
183: public function dispatchContent()
184: {
185: $this->dispatched = TRUE;
186: if (Helpers::isAjax()) {
187: header('X-Tracy-Ajax: 1');
188: }
189: if (preg_match('#^content(-ajax)?.(\w+)$#', isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : '', $m)) {
190: $session = & $_SESSION['_tracy']['bar'][$m[2] . $m[1]];
191: header('Content-Type: text/javascript');
192: header('Cache-Control: max-age=60');
193: header_remove('Set-Cookie');
194: if ($session) {
195: $method = $m[1] ? 'loadAjax' : 'init';
196: echo "Tracy.Debug.$method(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');';
197: $session = NULL;
198: }
199: $session = & $_SESSION['_tracy']['bluescreen'][$m[2]];
200: if ($session) {
201: echo "Tracy.BlueScreen.loadAjax(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');';
202: $session = NULL;
203: }
204: return TRUE;
205: }
206: }
207:
208: }
209: