@@ -537,6 +537,9 @@ |
||
537 | 537 | { |
538 | 538 | protected $str; |
539 | 539 | |
540 | + /** |
|
541 | + * @param string $str |
|
542 | + */ |
|
540 | 543 | public function __construct($str) |
541 | 544 | { |
542 | 545 | $this->str = $str; |
@@ -239,7 +239,7 @@ discard block |
||
239 | 239 | /** |
240 | 240 | * Gets the loaders. |
241 | 241 | * |
242 | - * @return array LoaderInterface[] |
|
242 | + * @return LoaderInterface[] LoaderInterface[] |
|
243 | 243 | */ |
244 | 244 | protected function getLoaders() |
245 | 245 | { |
@@ -298,6 +298,9 @@ discard block |
||
298 | 298 | $this->catalogues[$locale] = include $cache->getPath(); |
299 | 299 | } |
300 | 300 | |
301 | + /** |
|
302 | + * @param string $locale |
|
303 | + */ |
|
301 | 304 | private function dumpCatalogue($locale, ConfigCacheInterface $cache): void |
302 | 305 | { |
303 | 306 | $this->initializeCatalogue($locale); |
@@ -353,11 +356,17 @@ discard block |
||
353 | 356 | return $fallbackContent; |
354 | 357 | } |
355 | 358 | |
359 | + /** |
|
360 | + * @param string $locale |
|
361 | + */ |
|
356 | 362 | private function getCatalogueCachePath($locale) |
357 | 363 | { |
358 | 364 | return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php'; |
359 | 365 | } |
360 | 366 | |
367 | + /** |
|
368 | + * @param string $locale |
|
369 | + */ |
|
361 | 370 | private function doLoadCatalogue($locale): void |
362 | 371 | { |
363 | 372 | $this->catalogues[$locale] = new MessageCatalogue($locale); |
@@ -372,6 +381,9 @@ discard block |
||
372 | 381 | } |
373 | 382 | } |
374 | 383 | |
384 | + /** |
|
385 | + * @param string $locale |
|
386 | + */ |
|
375 | 387 | private function loadFallbackCatalogues($locale): void |
376 | 388 | { |
377 | 389 | $current = $this->catalogues[$locale]; |
@@ -284,7 +284,7 @@ |
||
284 | 284 | |
285 | 285 | $this->assertValidLocale($locale); |
286 | 286 | $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), |
287 | - function (ConfigCacheInterface $cache) use ($locale) { |
|
287 | + function(ConfigCacheInterface $cache) use ($locale) { |
|
288 | 288 | $this->dumpCatalogue($locale, $cache); |
289 | 289 | } |
290 | 290 | ); |
@@ -284,7 +284,7 @@ |
||
284 | 284 | |
285 | 285 | $this->assertValidLocale($locale); |
286 | 286 | $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), |
287 | - function (ConfigCacheInterface $cache) use ($locale) { |
|
287 | + function (ConfigCacheInterface $cache) use ($locale){ |
|
288 | 288 | $this->dumpCatalogue($locale, $cache); |
289 | 289 | } |
290 | 290 | ); |
@@ -313,7 +313,7 @@ discard block |
||
313 | 313 | %s |
314 | 314 | return \$catalogue; |
315 | 315 | |
316 | -EOF |
|
316 | +eof |
|
317 | 317 | , |
318 | 318 | $locale, |
319 | 319 | var_export($this->catalogues[$locale]->all(), true), |
@@ -338,7 +338,7 @@ discard block |
||
338 | 338 | $catalogue%s = new MessageCatalogue('%s', %s); |
339 | 339 | $catalogue%s->addFallbackCatalogue($catalogue%s); |
340 | 340 | |
341 | -EOF |
|
341 | +eof |
|
342 | 342 | , |
343 | 343 | $fallbackSuffix, |
344 | 344 | $fallback, |
@@ -55,6 +55,7 @@ |
||
55 | 55 | * @param string $locale The locale |
56 | 56 | * |
57 | 57 | * @throws InvalidArgumentException If the locale contains invalid characters |
58 | + * @return boolean|null |
|
58 | 59 | */ |
59 | 60 | public function setLocale($locale); |
60 | 61 |
@@ -29,6 +29,7 @@ |
||
29 | 29 | * @param array $options Options that are passed to the dumper |
30 | 30 | * |
31 | 31 | * @throws InvalidArgumentException |
32 | + * @return void |
|
32 | 33 | */ |
33 | 34 | public function write(MessageCatalogue $catalogue, $format, $options = array()); |
34 | 35 | } |
@@ -23,12 +23,19 @@ |
||
23 | 23 | } |
24 | 24 | |
25 | 25 | |
26 | + /** |
|
27 | + * @param boolean $arg1 |
|
28 | + * @param boolean $arg2 |
|
29 | + */ |
|
26 | 30 | public function second($arg1, $arg2) |
27 | 31 | { |
28 | 32 | self::third([1, 2, 3]); |
29 | 33 | } |
30 | 34 | |
31 | 35 | |
36 | + /** |
|
37 | + * @param integer[] $arg1 |
|
38 | + */ |
|
32 | 39 | public static function third($arg1) |
33 | 40 | { |
34 | 41 | throw new Exception('The my exception', 123); |
@@ -17,34 +17,34 @@ |
||
17 | 17 | |
18 | 18 | class DemoClass |
19 | 19 | { |
20 | - public function first($arg1, $arg2) |
|
21 | - { |
|
22 | - $this->second(true, false); |
|
23 | - } |
|
20 | + public function first($arg1, $arg2) |
|
21 | + { |
|
22 | + $this->second(true, false); |
|
23 | + } |
|
24 | 24 | |
25 | 25 | |
26 | - public function second($arg1, $arg2) |
|
27 | - { |
|
28 | - self::third([1, 2, 3]); |
|
29 | - } |
|
26 | + public function second($arg1, $arg2) |
|
27 | + { |
|
28 | + self::third([1, 2, 3]); |
|
29 | + } |
|
30 | 30 | |
31 | 31 | |
32 | - public static function third($arg1) |
|
33 | - { |
|
34 | - throw new Exception('The my exception', 123); |
|
35 | - } |
|
32 | + public static function third($arg1) |
|
33 | + { |
|
34 | + throw new Exception('The my exception', 123); |
|
35 | + } |
|
36 | 36 | } |
37 | 37 | |
38 | 38 | |
39 | 39 | function demo($a, $b) |
40 | 40 | { |
41 | - $demo = new DemoClass; |
|
42 | - $demo->first($a, $b); |
|
41 | + $demo = new DemoClass; |
|
42 | + $demo->first($a, $b); |
|
43 | 43 | } |
44 | 44 | |
45 | 45 | |
46 | 46 | if (Debugger::$productionMode) { |
47 | - echo '<p><b>For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.</b></p>'; |
|
47 | + echo '<p><b>For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.</b></p>'; |
|
48 | 48 | } |
49 | 49 | |
50 | 50 | demo(10, 'any string'); |
@@ -1,12 +1,12 @@ |
||
1 | 1 | <?php |
2 | 2 | |
3 | -require __DIR__ . '/../src/tracy.php'; |
|
3 | +require __DIR__.'/../src/tracy.php'; |
|
4 | 4 | |
5 | 5 | use Tracy\Debugger; |
6 | 6 | |
7 | 7 | // For security reasons, Tracy is visible only on localhost. |
8 | 8 | // You may force Tracy to run in development mode by passing the Debugger::DEVELOPMENT instead of Debugger::DETECT. |
9 | -Debugger::enable(Debugger::DETECT, __DIR__ . '/log'); |
|
9 | +Debugger::enable(Debugger::DETECT, __DIR__.'/log'); |
|
10 | 10 | |
11 | 11 | ?> |
12 | 12 | <!DOCTYPE html><link rel="stylesheet" href="assets/style.css"> |
@@ -17,12 +17,19 @@ |
||
17 | 17 | } |
18 | 18 | |
19 | 19 | |
20 | +/** |
|
21 | + * @param boolean $arg1 |
|
22 | + * @param boolean $arg2 |
|
23 | + */ |
|
20 | 24 | function second($arg1, $arg2) |
21 | 25 | { |
22 | 26 | third([1, 2, 3]); |
23 | 27 | } |
24 | 28 | |
25 | 29 | |
30 | +/** |
|
31 | + * @param integer[] $arg1 |
|
32 | + */ |
|
26 | 33 | function third($arg1) |
27 | 34 | { |
28 | 35 | throw new Exception('The my exception', 123); |
@@ -13,26 +13,26 @@ |
||
13 | 13 | |
14 | 14 | function first($arg1, $arg2) |
15 | 15 | { |
16 | - second(true, false); |
|
16 | + second(true, false); |
|
17 | 17 | } |
18 | 18 | |
19 | 19 | |
20 | 20 | function second($arg1, $arg2) |
21 | 21 | { |
22 | - third([1, 2, 3]); |
|
22 | + third([1, 2, 3]); |
|
23 | 23 | } |
24 | 24 | |
25 | 25 | |
26 | 26 | function third($arg1) |
27 | 27 | { |
28 | - throw new Exception('The my exception', 123); |
|
28 | + throw new Exception('The my exception', 123); |
|
29 | 29 | } |
30 | 30 | |
31 | 31 | |
32 | 32 | try { |
33 | - first(10, 'any string'); |
|
33 | + first(10, 'any string'); |
|
34 | 34 | } catch (Exception $e) { |
35 | - Debugger::fireLog($e); |
|
35 | + Debugger::fireLog($e); |
|
36 | 36 | } |
37 | 37 | |
38 | 38 | ?> |
@@ -1,6 +1,6 @@ |
||
1 | 1 | <?php |
2 | 2 | |
3 | -require __DIR__ . '/../src/tracy.php'; |
|
3 | +require __DIR__.'/../src/tracy.php'; |
|
4 | 4 | |
5 | 5 | use Tracy\Debugger; |
6 | 6 |
@@ -141,6 +141,7 @@ |
||
141 | 141 | |
142 | 142 | |
143 | 143 | /** |
144 | + * @param string $suffix |
|
144 | 145 | * @return array |
145 | 146 | */ |
146 | 147 | private function renderPanels($suffix = null) |
@@ -13,233 +13,233 @@ discard block |
||
13 | 13 | */ |
14 | 14 | class Bar |
15 | 15 | { |
16 | - /** @var IBarPanel[] */ |
|
17 | - private $panels = []; |
|
18 | - |
|
19 | - /** @var bool initialized by dispatchAssets() */ |
|
20 | - private $useSession = false; |
|
21 | - |
|
22 | - /** @var string|NULL generated by renderLoader() */ |
|
23 | - private $contentId; |
|
24 | - |
|
25 | - |
|
26 | - /** |
|
27 | - * Add custom panel. |
|
28 | - * @param IBarPanel $panel |
|
29 | - * @param string $id |
|
30 | - * @return static |
|
31 | - */ |
|
32 | - public function addPanel(IBarPanel $panel, $id = null) |
|
33 | - { |
|
34 | - if ($id === null) { |
|
35 | - $c = 0; |
|
36 | - do { |
|
37 | - $id = get_class($panel) . ($c++ ? "-$c" : ''); |
|
38 | - } while (isset($this->panels[$id])); |
|
39 | - } |
|
40 | - $this->panels[$id] = $panel; |
|
41 | - return $this; |
|
42 | - } |
|
43 | - |
|
44 | - |
|
45 | - /** |
|
46 | - * Returns panel with given id |
|
47 | - * @param string $id |
|
48 | - * @return IBarPanel|null |
|
49 | - */ |
|
50 | - public function getPanel($id) |
|
51 | - { |
|
52 | - return isset($this->panels[$id]) ? $this->panels[$id] : null; |
|
53 | - } |
|
54 | - |
|
55 | - |
|
56 | - /** |
|
57 | - * Renders loading <script> |
|
58 | - * @return void |
|
59 | - */ |
|
60 | - public function renderLoader() |
|
61 | - { |
|
62 | - if (!$this->useSession) { |
|
63 | - throw new \LogicException('Start session before Tracy is enabled.'); |
|
64 | - } |
|
65 | - $contentId = $this->contentId = $this->contentId ?: substr(md5(uniqid('', true)), 0, 10); |
|
66 | - $nonce = Helpers::getNonce(); |
|
67 | - $async = true; |
|
68 | - require __DIR__ . '/assets/Bar/loader.phtml'; |
|
69 | - } |
|
70 | - |
|
71 | - |
|
72 | - /** |
|
73 | - * Renders debug bar. |
|
74 | - * @return void |
|
75 | - */ |
|
76 | - public function render() |
|
77 | - { |
|
78 | - $useSession = $this->useSession && session_status() === PHP_SESSION_ACTIVE; |
|
79 | - $redirectQueue = &$_SESSION['_tracy']['redirect']; |
|
80 | - |
|
81 | - foreach (['bar', 'redirect', 'bluescreen'] as $key) { |
|
82 | - $queue = &$_SESSION['_tracy'][$key]; |
|
83 | - $queue = array_slice((array) $queue, -10, null, true); |
|
84 | - $queue = array_filter($queue, function ($item) { |
|
85 | - return isset($item['time']) && $item['time'] > time() - 60; |
|
86 | - }); |
|
87 | - } |
|
88 | - |
|
89 | - $rows = []; |
|
90 | - |
|
91 | - if (Helpers::isAjax()) { |
|
92 | - if ($useSession) { |
|
93 | - $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')]; |
|
94 | - $contentId = $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax'; |
|
95 | - $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
|
96 | - } |
|
97 | - |
|
98 | - } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect |
|
99 | - if ($useSession) { |
|
100 | - Dumper::fetchLiveData(); |
|
101 | - Dumper::$livePrefix = count($redirectQueue) . 'p'; |
|
102 | - $redirectQueue[] = [ |
|
103 | - 'panels' => $this->renderPanels('-r' . count($redirectQueue)), |
|
104 | - 'dumps' => Dumper::fetchLiveData(), |
|
105 | - 'time' => time(), |
|
106 | - ]; |
|
107 | - } |
|
108 | - |
|
109 | - } elseif (Helpers::isHtmlMode()) { |
|
110 | - $rows[] = (object) ['type' => 'main', 'panels' => $this->renderPanels()]; |
|
111 | - $dumps = Dumper::fetchLiveData(); |
|
112 | - foreach (array_reverse((array) $redirectQueue) as $info) { |
|
113 | - $rows[] = (object) ['type' => 'redirect', 'panels' => $info['panels']]; |
|
114 | - $dumps += $info['dumps']; |
|
115 | - } |
|
116 | - $redirectQueue = null; |
|
117 | - $content = self::renderHtmlRows($rows); |
|
118 | - |
|
119 | - if ($this->contentId) { |
|
120 | - $_SESSION['_tracy']['bar'][$this->contentId] = ['content' => $content, 'dumps' => $dumps, 'time' => time()]; |
|
121 | - } else { |
|
122 | - $contentId = substr(md5(uniqid('', true)), 0, 10); |
|
123 | - $nonce = Helpers::getNonce(); |
|
124 | - $async = false; |
|
125 | - require __DIR__ . '/assets/Bar/loader.phtml'; |
|
126 | - } |
|
127 | - } |
|
128 | - } |
|
129 | - |
|
130 | - |
|
131 | - /** |
|
132 | - * @return string |
|
133 | - */ |
|
134 | - private static function renderHtmlRows(array $rows) |
|
135 | - { |
|
136 | - ob_start(function () {}); |
|
137 | - require __DIR__ . '/assets/Bar/panels.phtml'; |
|
138 | - require __DIR__ . '/assets/Bar/bar.phtml'; |
|
139 | - return Helpers::fixEncoding(ob_get_clean()); |
|
140 | - } |
|
141 | - |
|
142 | - |
|
143 | - /** |
|
144 | - * @return array |
|
145 | - */ |
|
146 | - private function renderPanels($suffix = null) |
|
147 | - { |
|
148 | - set_error_handler(function ($severity, $message, $file, $line) { |
|
149 | - if (error_reporting() & $severity) { |
|
150 | - throw new \ErrorException($message, 0, $severity, $file, $line); |
|
151 | - } |
|
152 | - }); |
|
153 | - |
|
154 | - $obLevel = ob_get_level(); |
|
155 | - $panels = []; |
|
156 | - |
|
157 | - foreach ($this->panels as $id => $panel) { |
|
158 | - $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix; |
|
159 | - try { |
|
160 | - $tab = (string) $panel->getTab(); |
|
161 | - $panelHtml = $tab ? (string) $panel->getPanel() : null; |
|
162 | - if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) { |
|
163 | - $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated'); |
|
164 | - } |
|
165 | - |
|
166 | - } catch (\Exception $e) { |
|
167 | - } catch (\Throwable $e) { |
|
168 | - } |
|
169 | - if (isset($e)) { |
|
170 | - while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
171 | - ob_end_clean(); |
|
172 | - } |
|
173 | - $idHtml = "error-$idHtml"; |
|
174 | - $tab = "Error in $id"; |
|
175 | - $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>'; |
|
176 | - unset($e); |
|
177 | - } |
|
178 | - $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml]; |
|
179 | - } |
|
180 | - |
|
181 | - restore_error_handler(); |
|
182 | - return $panels; |
|
183 | - } |
|
184 | - |
|
185 | - |
|
186 | - /** |
|
187 | - * Renders debug bar assets. |
|
188 | - * @return bool |
|
189 | - */ |
|
190 | - public function dispatchAssets() |
|
191 | - { |
|
192 | - $asset = isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : null; |
|
193 | - if ($asset === 'js') { |
|
194 | - header('Content-Type: application/javascript'); |
|
195 | - header('Cache-Control: max-age=864000'); |
|
196 | - header_remove('Pragma'); |
|
197 | - header_remove('Set-Cookie'); |
|
198 | - $this->renderAssets(); |
|
199 | - return true; |
|
200 | - } |
|
201 | - |
|
202 | - $this->useSession = session_status() === PHP_SESSION_ACTIVE; |
|
203 | - |
|
204 | - if ($this->useSession && Helpers::isAjax()) { |
|
205 | - header('X-Tracy-Ajax: 1'); // session must be already locked |
|
206 | - } |
|
207 | - |
|
208 | - if ($this->useSession && $asset && preg_match('#^content(-ajax)?\.(\w+)$#', $asset, $m)) { |
|
209 | - $session = &$_SESSION['_tracy']['bar'][$m[2] . $m[1]]; |
|
210 | - header('Content-Type: application/javascript'); |
|
211 | - header('Cache-Control: max-age=60'); |
|
212 | - header_remove('Set-Cookie'); |
|
213 | - if (!$m[1]) { |
|
214 | - $this->renderAssets(); |
|
215 | - } |
|
216 | - if ($session) { |
|
217 | - $method = $m[1] ? 'loadAjax' : 'init'; |
|
218 | - echo "Tracy.Debug.$method(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; |
|
219 | - $session = null; |
|
220 | - } |
|
221 | - $session = &$_SESSION['_tracy']['bluescreen'][$m[2]]; |
|
222 | - if ($session) { |
|
223 | - echo 'Tracy.BlueScreen.loadAjax(', json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; |
|
224 | - $session = null; |
|
225 | - } |
|
226 | - return true; |
|
227 | - } |
|
228 | - |
|
229 | - return false; |
|
230 | - } |
|
231 | - |
|
232 | - |
|
233 | - private function renderAssets() |
|
234 | - { |
|
235 | - $css = array_map('file_get_contents', array_merge([ |
|
236 | - __DIR__ . '/assets/Bar/bar.css', |
|
237 | - __DIR__ . '/assets/Toggle/toggle.css', |
|
238 | - __DIR__ . '/assets/Dumper/dumper.css', |
|
239 | - __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
240 | - ], Debugger::$customCssFiles)); |
|
241 | - |
|
242 | - echo |
|
16 | + /** @var IBarPanel[] */ |
|
17 | + private $panels = []; |
|
18 | + |
|
19 | + /** @var bool initialized by dispatchAssets() */ |
|
20 | + private $useSession = false; |
|
21 | + |
|
22 | + /** @var string|NULL generated by renderLoader() */ |
|
23 | + private $contentId; |
|
24 | + |
|
25 | + |
|
26 | + /** |
|
27 | + * Add custom panel. |
|
28 | + * @param IBarPanel $panel |
|
29 | + * @param string $id |
|
30 | + * @return static |
|
31 | + */ |
|
32 | + public function addPanel(IBarPanel $panel, $id = null) |
|
33 | + { |
|
34 | + if ($id === null) { |
|
35 | + $c = 0; |
|
36 | + do { |
|
37 | + $id = get_class($panel) . ($c++ ? "-$c" : ''); |
|
38 | + } while (isset($this->panels[$id])); |
|
39 | + } |
|
40 | + $this->panels[$id] = $panel; |
|
41 | + return $this; |
|
42 | + } |
|
43 | + |
|
44 | + |
|
45 | + /** |
|
46 | + * Returns panel with given id |
|
47 | + * @param string $id |
|
48 | + * @return IBarPanel|null |
|
49 | + */ |
|
50 | + public function getPanel($id) |
|
51 | + { |
|
52 | + return isset($this->panels[$id]) ? $this->panels[$id] : null; |
|
53 | + } |
|
54 | + |
|
55 | + |
|
56 | + /** |
|
57 | + * Renders loading <script> |
|
58 | + * @return void |
|
59 | + */ |
|
60 | + public function renderLoader() |
|
61 | + { |
|
62 | + if (!$this->useSession) { |
|
63 | + throw new \LogicException('Start session before Tracy is enabled.'); |
|
64 | + } |
|
65 | + $contentId = $this->contentId = $this->contentId ?: substr(md5(uniqid('', true)), 0, 10); |
|
66 | + $nonce = Helpers::getNonce(); |
|
67 | + $async = true; |
|
68 | + require __DIR__ . '/assets/Bar/loader.phtml'; |
|
69 | + } |
|
70 | + |
|
71 | + |
|
72 | + /** |
|
73 | + * Renders debug bar. |
|
74 | + * @return void |
|
75 | + */ |
|
76 | + public function render() |
|
77 | + { |
|
78 | + $useSession = $this->useSession && session_status() === PHP_SESSION_ACTIVE; |
|
79 | + $redirectQueue = &$_SESSION['_tracy']['redirect']; |
|
80 | + |
|
81 | + foreach (['bar', 'redirect', 'bluescreen'] as $key) { |
|
82 | + $queue = &$_SESSION['_tracy'][$key]; |
|
83 | + $queue = array_slice((array) $queue, -10, null, true); |
|
84 | + $queue = array_filter($queue, function ($item) { |
|
85 | + return isset($item['time']) && $item['time'] > time() - 60; |
|
86 | + }); |
|
87 | + } |
|
88 | + |
|
89 | + $rows = []; |
|
90 | + |
|
91 | + if (Helpers::isAjax()) { |
|
92 | + if ($useSession) { |
|
93 | + $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')]; |
|
94 | + $contentId = $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax'; |
|
95 | + $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
|
96 | + } |
|
97 | + |
|
98 | + } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect |
|
99 | + if ($useSession) { |
|
100 | + Dumper::fetchLiveData(); |
|
101 | + Dumper::$livePrefix = count($redirectQueue) . 'p'; |
|
102 | + $redirectQueue[] = [ |
|
103 | + 'panels' => $this->renderPanels('-r' . count($redirectQueue)), |
|
104 | + 'dumps' => Dumper::fetchLiveData(), |
|
105 | + 'time' => time(), |
|
106 | + ]; |
|
107 | + } |
|
108 | + |
|
109 | + } elseif (Helpers::isHtmlMode()) { |
|
110 | + $rows[] = (object) ['type' => 'main', 'panels' => $this->renderPanels()]; |
|
111 | + $dumps = Dumper::fetchLiveData(); |
|
112 | + foreach (array_reverse((array) $redirectQueue) as $info) { |
|
113 | + $rows[] = (object) ['type' => 'redirect', 'panels' => $info['panels']]; |
|
114 | + $dumps += $info['dumps']; |
|
115 | + } |
|
116 | + $redirectQueue = null; |
|
117 | + $content = self::renderHtmlRows($rows); |
|
118 | + |
|
119 | + if ($this->contentId) { |
|
120 | + $_SESSION['_tracy']['bar'][$this->contentId] = ['content' => $content, 'dumps' => $dumps, 'time' => time()]; |
|
121 | + } else { |
|
122 | + $contentId = substr(md5(uniqid('', true)), 0, 10); |
|
123 | + $nonce = Helpers::getNonce(); |
|
124 | + $async = false; |
|
125 | + require __DIR__ . '/assets/Bar/loader.phtml'; |
|
126 | + } |
|
127 | + } |
|
128 | + } |
|
129 | + |
|
130 | + |
|
131 | + /** |
|
132 | + * @return string |
|
133 | + */ |
|
134 | + private static function renderHtmlRows(array $rows) |
|
135 | + { |
|
136 | + ob_start(function () {}); |
|
137 | + require __DIR__ . '/assets/Bar/panels.phtml'; |
|
138 | + require __DIR__ . '/assets/Bar/bar.phtml'; |
|
139 | + return Helpers::fixEncoding(ob_get_clean()); |
|
140 | + } |
|
141 | + |
|
142 | + |
|
143 | + /** |
|
144 | + * @return array |
|
145 | + */ |
|
146 | + private function renderPanels($suffix = null) |
|
147 | + { |
|
148 | + set_error_handler(function ($severity, $message, $file, $line) { |
|
149 | + if (error_reporting() & $severity) { |
|
150 | + throw new \ErrorException($message, 0, $severity, $file, $line); |
|
151 | + } |
|
152 | + }); |
|
153 | + |
|
154 | + $obLevel = ob_get_level(); |
|
155 | + $panels = []; |
|
156 | + |
|
157 | + foreach ($this->panels as $id => $panel) { |
|
158 | + $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix; |
|
159 | + try { |
|
160 | + $tab = (string) $panel->getTab(); |
|
161 | + $panelHtml = $tab ? (string) $panel->getPanel() : null; |
|
162 | + if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) { |
|
163 | + $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated'); |
|
164 | + } |
|
165 | + |
|
166 | + } catch (\Exception $e) { |
|
167 | + } catch (\Throwable $e) { |
|
168 | + } |
|
169 | + if (isset($e)) { |
|
170 | + while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
171 | + ob_end_clean(); |
|
172 | + } |
|
173 | + $idHtml = "error-$idHtml"; |
|
174 | + $tab = "Error in $id"; |
|
175 | + $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>'; |
|
176 | + unset($e); |
|
177 | + } |
|
178 | + $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml]; |
|
179 | + } |
|
180 | + |
|
181 | + restore_error_handler(); |
|
182 | + return $panels; |
|
183 | + } |
|
184 | + |
|
185 | + |
|
186 | + /** |
|
187 | + * Renders debug bar assets. |
|
188 | + * @return bool |
|
189 | + */ |
|
190 | + public function dispatchAssets() |
|
191 | + { |
|
192 | + $asset = isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : null; |
|
193 | + if ($asset === 'js') { |
|
194 | + header('Content-Type: application/javascript'); |
|
195 | + header('Cache-Control: max-age=864000'); |
|
196 | + header_remove('Pragma'); |
|
197 | + header_remove('Set-Cookie'); |
|
198 | + $this->renderAssets(); |
|
199 | + return true; |
|
200 | + } |
|
201 | + |
|
202 | + $this->useSession = session_status() === PHP_SESSION_ACTIVE; |
|
203 | + |
|
204 | + if ($this->useSession && Helpers::isAjax()) { |
|
205 | + header('X-Tracy-Ajax: 1'); // session must be already locked |
|
206 | + } |
|
207 | + |
|
208 | + if ($this->useSession && $asset && preg_match('#^content(-ajax)?\.(\w+)$#', $asset, $m)) { |
|
209 | + $session = &$_SESSION['_tracy']['bar'][$m[2] . $m[1]]; |
|
210 | + header('Content-Type: application/javascript'); |
|
211 | + header('Cache-Control: max-age=60'); |
|
212 | + header_remove('Set-Cookie'); |
|
213 | + if (!$m[1]) { |
|
214 | + $this->renderAssets(); |
|
215 | + } |
|
216 | + if ($session) { |
|
217 | + $method = $m[1] ? 'loadAjax' : 'init'; |
|
218 | + echo "Tracy.Debug.$method(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; |
|
219 | + $session = null; |
|
220 | + } |
|
221 | + $session = &$_SESSION['_tracy']['bluescreen'][$m[2]]; |
|
222 | + if ($session) { |
|
223 | + echo 'Tracy.BlueScreen.loadAjax(', json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; |
|
224 | + $session = null; |
|
225 | + } |
|
226 | + return true; |
|
227 | + } |
|
228 | + |
|
229 | + return false; |
|
230 | + } |
|
231 | + |
|
232 | + |
|
233 | + private function renderAssets() |
|
234 | + { |
|
235 | + $css = array_map('file_get_contents', array_merge([ |
|
236 | + __DIR__ . '/assets/Bar/bar.css', |
|
237 | + __DIR__ . '/assets/Toggle/toggle.css', |
|
238 | + __DIR__ . '/assets/Dumper/dumper.css', |
|
239 | + __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
240 | + ], Debugger::$customCssFiles)); |
|
241 | + |
|
242 | + echo |
|
243 | 243 | "(function(){ |
244 | 244 | var el = document.createElement('style'); |
245 | 245 | el.setAttribute('nonce', document.currentScript.getAttribute('nonce') || document.currentScript.nonce); |
@@ -248,11 +248,11 @@ discard block |
||
248 | 248 | document.head.appendChild(el);}) |
249 | 249 | ();\n"; |
250 | 250 | |
251 | - array_map('readfile', array_merge([ |
|
252 | - __DIR__ . '/assets/Bar/bar.js', |
|
253 | - __DIR__ . '/assets/Toggle/toggle.js', |
|
254 | - __DIR__ . '/assets/Dumper/dumper.js', |
|
255 | - __DIR__ . '/assets/BlueScreen/bluescreen.js', |
|
256 | - ], Debugger::$customJsFiles)); |
|
257 | - } |
|
251 | + array_map('readfile', array_merge([ |
|
252 | + __DIR__ . '/assets/Bar/bar.js', |
|
253 | + __DIR__ . '/assets/Toggle/toggle.js', |
|
254 | + __DIR__ . '/assets/Dumper/dumper.js', |
|
255 | + __DIR__ . '/assets/BlueScreen/bluescreen.js', |
|
256 | + ], Debugger::$customJsFiles)); |
|
257 | + } |
|
258 | 258 | } |
@@ -34,7 +34,7 @@ discard block |
||
34 | 34 | if ($id === null) { |
35 | 35 | $c = 0; |
36 | 36 | do { |
37 | - $id = get_class($panel) . ($c++ ? "-$c" : ''); |
|
37 | + $id = get_class($panel).($c++ ? "-$c" : ''); |
|
38 | 38 | } while (isset($this->panels[$id])); |
39 | 39 | } |
40 | 40 | $this->panels[$id] = $panel; |
@@ -65,7 +65,7 @@ discard block |
||
65 | 65 | $contentId = $this->contentId = $this->contentId ?: substr(md5(uniqid('', true)), 0, 10); |
66 | 66 | $nonce = Helpers::getNonce(); |
67 | 67 | $async = true; |
68 | - require __DIR__ . '/assets/Bar/loader.phtml'; |
|
68 | + require __DIR__.'/assets/Bar/loader.phtml'; |
|
69 | 69 | } |
70 | 70 | |
71 | 71 | |
@@ -81,7 +81,7 @@ discard block |
||
81 | 81 | foreach (['bar', 'redirect', 'bluescreen'] as $key) { |
82 | 82 | $queue = &$_SESSION['_tracy'][$key]; |
83 | 83 | $queue = array_slice((array) $queue, -10, null, true); |
84 | - $queue = array_filter($queue, function ($item) { |
|
84 | + $queue = array_filter($queue, function($item){ |
|
85 | 85 | return isset($item['time']) && $item['time'] > time() - 60; |
86 | 86 | }); |
87 | 87 | } |
@@ -91,16 +91,16 @@ discard block |
||
91 | 91 | if (Helpers::isAjax()) { |
92 | 92 | if ($useSession) { |
93 | 93 | $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')]; |
94 | - $contentId = $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax'; |
|
94 | + $contentId = $_SERVER['HTTP_X_TRACY_AJAX'].'-ajax'; |
|
95 | 95 | $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
96 | 96 | } |
97 | 97 | |
98 | 98 | } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect |
99 | 99 | if ($useSession) { |
100 | 100 | Dumper::fetchLiveData(); |
101 | - Dumper::$livePrefix = count($redirectQueue) . 'p'; |
|
101 | + Dumper::$livePrefix = count($redirectQueue).'p'; |
|
102 | 102 | $redirectQueue[] = [ |
103 | - 'panels' => $this->renderPanels('-r' . count($redirectQueue)), |
|
103 | + 'panels' => $this->renderPanels('-r'.count($redirectQueue)), |
|
104 | 104 | 'dumps' => Dumper::fetchLiveData(), |
105 | 105 | 'time' => time(), |
106 | 106 | ]; |
@@ -122,7 +122,7 @@ discard block |
||
122 | 122 | $contentId = substr(md5(uniqid('', true)), 0, 10); |
123 | 123 | $nonce = Helpers::getNonce(); |
124 | 124 | $async = false; |
125 | - require __DIR__ . '/assets/Bar/loader.phtml'; |
|
125 | + require __DIR__.'/assets/Bar/loader.phtml'; |
|
126 | 126 | } |
127 | 127 | } |
128 | 128 | } |
@@ -133,9 +133,9 @@ discard block |
||
133 | 133 | */ |
134 | 134 | private static function renderHtmlRows(array $rows) |
135 | 135 | { |
136 | - ob_start(function () {}); |
|
137 | - require __DIR__ . '/assets/Bar/panels.phtml'; |
|
138 | - require __DIR__ . '/assets/Bar/bar.phtml'; |
|
136 | + ob_start(function(){}); |
|
137 | + require __DIR__.'/assets/Bar/panels.phtml'; |
|
138 | + require __DIR__.'/assets/Bar/bar.phtml'; |
|
139 | 139 | return Helpers::fixEncoding(ob_get_clean()); |
140 | 140 | } |
141 | 141 | |
@@ -145,7 +145,7 @@ discard block |
||
145 | 145 | */ |
146 | 146 | private function renderPanels($suffix = null) |
147 | 147 | { |
148 | - set_error_handler(function ($severity, $message, $file, $line) { |
|
148 | + set_error_handler(function($severity, $message, $file, $line){ |
|
149 | 149 | if (error_reporting() & $severity) { |
150 | 150 | throw new \ErrorException($message, 0, $severity, $file, $line); |
151 | 151 | } |
@@ -155,7 +155,7 @@ discard block |
||
155 | 155 | $panels = []; |
156 | 156 | |
157 | 157 | foreach ($this->panels as $id => $panel) { |
158 | - $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix; |
|
158 | + $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id).$suffix; |
|
159 | 159 | try { |
160 | 160 | $tab = (string) $panel->getTab(); |
161 | 161 | $panelHtml = $tab ? (string) $panel->getPanel() : null; |
@@ -172,7 +172,7 @@ discard block |
||
172 | 172 | } |
173 | 173 | $idHtml = "error-$idHtml"; |
174 | 174 | $tab = "Error in $id"; |
175 | - $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>'; |
|
175 | + $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>".nl2br(Helpers::escapeHtml($e)).'</div>'; |
|
176 | 176 | unset($e); |
177 | 177 | } |
178 | 178 | $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml]; |
@@ -206,7 +206,7 @@ discard block |
||
206 | 206 | } |
207 | 207 | |
208 | 208 | if ($this->useSession && $asset && preg_match('#^content(-ajax)?\.(\w+)$#', $asset, $m)) { |
209 | - $session = &$_SESSION['_tracy']['bar'][$m[2] . $m[1]]; |
|
209 | + $session = &$_SESSION['_tracy']['bar'][$m[2].$m[1]]; |
|
210 | 210 | header('Content-Type: application/javascript'); |
211 | 211 | header('Cache-Control: max-age=60'); |
212 | 212 | header_remove('Set-Cookie'); |
@@ -233,10 +233,10 @@ discard block |
||
233 | 233 | private function renderAssets() |
234 | 234 | { |
235 | 235 | $css = array_map('file_get_contents', array_merge([ |
236 | - __DIR__ . '/assets/Bar/bar.css', |
|
237 | - __DIR__ . '/assets/Toggle/toggle.css', |
|
238 | - __DIR__ . '/assets/Dumper/dumper.css', |
|
239 | - __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
236 | + __DIR__.'/assets/Bar/bar.css', |
|
237 | + __DIR__.'/assets/Toggle/toggle.css', |
|
238 | + __DIR__.'/assets/Dumper/dumper.css', |
|
239 | + __DIR__.'/assets/BlueScreen/bluescreen.css', |
|
240 | 240 | ], Debugger::$customCssFiles)); |
241 | 241 | |
242 | 242 | echo |
@@ -244,15 +244,15 @@ discard block |
||
244 | 244 | var el = document.createElement('style'); |
245 | 245 | el.setAttribute('nonce', document.currentScript.getAttribute('nonce') || document.currentScript.nonce); |
246 | 246 | el.className='tracy-debug'; |
247 | - el.textContent=" . json_encode(preg_replace('#\s+#u', ' ', implode($css))) . "; |
|
247 | + el.textContent=" . json_encode(preg_replace('#\s+#u', ' ', implode($css)))."; |
|
248 | 248 | document.head.appendChild(el);}) |
249 | 249 | ();\n"; |
250 | 250 | |
251 | 251 | array_map('readfile', array_merge([ |
252 | - __DIR__ . '/assets/Bar/bar.js', |
|
253 | - __DIR__ . '/assets/Toggle/toggle.js', |
|
254 | - __DIR__ . '/assets/Dumper/dumper.js', |
|
255 | - __DIR__ . '/assets/BlueScreen/bluescreen.js', |
|
252 | + __DIR__.'/assets/Bar/bar.js', |
|
253 | + __DIR__.'/assets/Toggle/toggle.js', |
|
254 | + __DIR__.'/assets/Dumper/dumper.js', |
|
255 | + __DIR__.'/assets/BlueScreen/bluescreen.js', |
|
256 | 256 | ], Debugger::$customJsFiles)); |
257 | 257 | } |
258 | 258 | } |
@@ -81,7 +81,7 @@ discard block |
||
81 | 81 | foreach (['bar', 'redirect', 'bluescreen'] as $key) { |
82 | 82 | $queue = &$_SESSION['_tracy'][$key]; |
83 | 83 | $queue = array_slice((array) $queue, -10, null, true); |
84 | - $queue = array_filter($queue, function ($item) { |
|
84 | + $queue = array_filter($queue, function ($item){ |
|
85 | 85 | return isset($item['time']) && $item['time'] > time() - 60; |
86 | 86 | }); |
87 | 87 | } |
@@ -95,7 +95,8 @@ discard block |
||
95 | 95 | $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
96 | 96 | } |
97 | 97 | |
98 | - } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect |
|
98 | + } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { |
|
99 | +// redirect |
|
99 | 100 | if ($useSession) { |
100 | 101 | Dumper::fetchLiveData(); |
101 | 102 | Dumper::$livePrefix = count($redirectQueue) . 'p'; |
@@ -145,7 +146,7 @@ discard block |
||
145 | 146 | */ |
146 | 147 | private function renderPanels($suffix = null) |
147 | 148 | { |
148 | - set_error_handler(function ($severity, $message, $file, $line) { |
|
149 | + set_error_handler(function ($severity, $message, $file, $line){ |
|
149 | 150 | if (error_reporting() & $severity) { |
150 | 151 | throw new \ErrorException($message, 0, $severity, $file, $line); |
151 | 152 | } |
@@ -167,7 +168,8 @@ discard block |
||
167 | 168 | } catch (\Throwable $e) { |
168 | 169 | } |
169 | 170 | if (isset($e)) { |
170 | - while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
171 | + while (ob_get_level() > $obLevel) { |
|
172 | +// restore ob-level if broken |
|
171 | 173 | ob_end_clean(); |
172 | 174 | } |
173 | 175 | $idHtml = "error-$idHtml"; |
@@ -107,6 +107,10 @@ |
||
107 | 107 | } |
108 | 108 | |
109 | 109 | |
110 | + /** |
|
111 | + * @param \Throwable $exception |
|
112 | + * @param string $template |
|
113 | + */ |
|
110 | 114 | private function renderTemplate($exception, $template, $toScreen = true) |
111 | 115 | { |
112 | 116 | $messageHtml = preg_replace( |
@@ -13,347 +13,347 @@ |
||
13 | 13 | */ |
14 | 14 | class BlueScreen |
15 | 15 | { |
16 | - /** @var string[] */ |
|
17 | - public $info = []; |
|
18 | - |
|
19 | - /** @var string[] paths to be collapsed in stack trace (e.g. core libraries) */ |
|
20 | - public $collapsePaths = []; |
|
21 | - |
|
22 | - /** @var int */ |
|
23 | - public $maxDepth = 3; |
|
24 | - |
|
25 | - /** @var int */ |
|
26 | - public $maxLength = 150; |
|
27 | - |
|
28 | - /** @var string[] */ |
|
29 | - public $keysToHide = ['password', 'passwd', 'pass', 'pwd', 'creditcard', 'credit card', 'cc', 'pin']; |
|
30 | - |
|
31 | - /** @var callable[] */ |
|
32 | - private $panels = []; |
|
33 | - |
|
34 | - /** @var callable[] functions that returns action for exceptions */ |
|
35 | - private $actions = []; |
|
36 | - |
|
37 | - |
|
38 | - public function __construct() |
|
39 | - { |
|
40 | - $this->collapsePaths[] = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy$#', strtr(__DIR__, '\\', '/'), $m) |
|
41 | - ? $m[1] |
|
42 | - : __DIR__; |
|
43 | - } |
|
44 | - |
|
45 | - |
|
46 | - /** |
|
47 | - * Add custom panel. |
|
48 | - * @param callable $panel |
|
49 | - * @return static |
|
50 | - */ |
|
51 | - public function addPanel($panel) |
|
52 | - { |
|
53 | - if (!in_array($panel, $this->panels, true)) { |
|
54 | - $this->panels[] = $panel; |
|
55 | - } |
|
56 | - return $this; |
|
57 | - } |
|
58 | - |
|
59 | - |
|
60 | - /** |
|
61 | - * Add action. |
|
62 | - * @param callable $action |
|
63 | - * @return static |
|
64 | - */ |
|
65 | - public function addAction($action) |
|
66 | - { |
|
67 | - $this->actions[] = $action; |
|
68 | - return $this; |
|
69 | - } |
|
70 | - |
|
71 | - |
|
72 | - /** |
|
73 | - * Renders blue screen. |
|
74 | - * @param \Exception|\Throwable $exception |
|
75 | - * @return void |
|
76 | - */ |
|
77 | - public function render($exception) |
|
78 | - { |
|
79 | - if (Helpers::isAjax() && session_status() === PHP_SESSION_ACTIVE) { |
|
80 | - ob_start(function () {}); |
|
81 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/content.phtml'); |
|
82 | - $contentId = $_SERVER['HTTP_X_TRACY_AJAX']; |
|
83 | - $_SESSION['_tracy']['bluescreen'][$contentId] = ['content' => ob_get_clean(), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
|
84 | - |
|
85 | - } else { |
|
86 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml'); |
|
87 | - } |
|
88 | - } |
|
89 | - |
|
90 | - |
|
91 | - /** |
|
92 | - * Renders blue screen to file (if file exists, it will not be overwritten). |
|
93 | - * @param \Exception|\Throwable $exception |
|
94 | - * @param string $file file path |
|
95 | - * @return void |
|
96 | - */ |
|
97 | - public function renderToFile($exception, $file) |
|
98 | - { |
|
99 | - if ($handle = @fopen($file, 'x')) { |
|
100 | - ob_start(); // double buffer prevents sending HTTP headers in some PHP |
|
101 | - ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); |
|
102 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml', false); |
|
103 | - ob_end_flush(); |
|
104 | - ob_end_clean(); |
|
105 | - fclose($handle); |
|
106 | - } |
|
107 | - } |
|
108 | - |
|
109 | - |
|
110 | - private function renderTemplate($exception, $template, $toScreen = true) |
|
111 | - { |
|
112 | - $messageHtml = preg_replace( |
|
113 | - '#\'\S[^\']*\S\'|"\S[^"]*\S"#U', |
|
114 | - '<i>$0</i>', |
|
115 | - htmlspecialchars((string) $exception->getMessage(), ENT_SUBSTITUTE, 'UTF-8') |
|
116 | - ); |
|
117 | - $info = array_filter($this->info); |
|
118 | - $source = Helpers::getSource(); |
|
119 | - $sourceIsUrl = preg_match('#^https?://#', $source); |
|
120 | - $title = $exception instanceof \ErrorException |
|
121 | - ? Helpers::errorTypeToString($exception->getSeverity()) |
|
122 | - : Helpers::getClass($exception); |
|
123 | - $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? null : error_get_last(); |
|
124 | - |
|
125 | - $keysToHide = array_flip(array_map('strtolower', $this->keysToHide)); |
|
126 | - $dump = function ($v, $k = null) use ($keysToHide) { |
|
127 | - if (is_string($k) && isset($keysToHide[strtolower($k)])) { |
|
128 | - $v = Dumper::HIDDEN_VALUE; |
|
129 | - } |
|
130 | - return Dumper::toHtml($v, [ |
|
131 | - Dumper::DEPTH => $this->maxDepth, |
|
132 | - Dumper::TRUNCATE => $this->maxLength, |
|
133 | - Dumper::LIVE => true, |
|
134 | - Dumper::LOCATION => Dumper::LOCATION_CLASS, |
|
135 | - Dumper::KEYS_TO_HIDE => $this->keysToHide, |
|
136 | - ]); |
|
137 | - }; |
|
138 | - $css = array_map('file_get_contents', array_merge([ |
|
139 | - __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
140 | - ], Debugger::$customCssFiles)); |
|
141 | - $css = preg_replace('#\s+#u', ' ', implode($css)); |
|
142 | - |
|
143 | - $nonce = $toScreen ? Helpers::getNonce() : null; |
|
144 | - $actions = $toScreen ? $this->renderActions($exception) : []; |
|
145 | - |
|
146 | - require $template; |
|
147 | - } |
|
148 | - |
|
149 | - |
|
150 | - /** |
|
151 | - * @return \stdClass[] |
|
152 | - */ |
|
153 | - private function renderPanels($ex) |
|
154 | - { |
|
155 | - $obLevel = ob_get_level(); |
|
156 | - $res = []; |
|
157 | - foreach ($this->panels as $callback) { |
|
158 | - try { |
|
159 | - $panel = call_user_func($callback, $ex); |
|
160 | - if (empty($panel['tab']) || empty($panel['panel'])) { |
|
161 | - continue; |
|
162 | - } |
|
163 | - $res[] = (object) $panel; |
|
164 | - continue; |
|
165 | - } catch (\Exception $e) { |
|
166 | - } catch (\Throwable $e) { |
|
167 | - } |
|
168 | - while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
169 | - ob_end_clean(); |
|
170 | - } |
|
171 | - is_callable($callback, true, $name); |
|
172 | - $res[] = (object) [ |
|
173 | - 'tab' => "Error in panel $name", |
|
174 | - 'panel' => nl2br(Helpers::escapeHtml($e)), |
|
175 | - ]; |
|
176 | - } |
|
177 | - return $res; |
|
178 | - } |
|
179 | - |
|
180 | - |
|
181 | - /** |
|
182 | - * @return array[] |
|
183 | - */ |
|
184 | - private function renderActions($ex) |
|
185 | - { |
|
186 | - $actions = []; |
|
187 | - foreach ($this->actions as $callback) { |
|
188 | - $action = call_user_func($callback, $ex); |
|
189 | - if (!empty($action['link']) && !empty($action['label'])) { |
|
190 | - $actions[] = $action; |
|
191 | - } |
|
192 | - } |
|
193 | - |
|
194 | - if (property_exists($ex, 'tracyAction') && !empty($ex->tracyAction['link']) && !empty($ex->tracyAction['label'])) { |
|
195 | - $actions[] = $ex->tracyAction; |
|
196 | - } |
|
197 | - |
|
198 | - if (preg_match('# ([\'"])(\w{3,}(?:\\\\\w{3,})+)\\1#i', $ex->getMessage(), $m)) { |
|
199 | - $class = $m[2]; |
|
200 | - if ( |
|
201 | - !class_exists($class) && !interface_exists($class) && !trait_exists($class) |
|
202 | - && ($file = Helpers::guessClassFile($class)) && !is_file($file) |
|
203 | - ) { |
|
204 | - $actions[] = [ |
|
205 | - 'link' => Helpers::editorUri($file, 1, 'create'), |
|
206 | - 'label' => 'create class', |
|
207 | - ]; |
|
208 | - } |
|
209 | - } |
|
210 | - |
|
211 | - if (preg_match('# ([\'"])((?:/|[a-z]:[/\\\\])\w[^\'"]+\.\w{2,5})\\1#i', $ex->getMessage(), $m)) { |
|
212 | - $file = $m[2]; |
|
213 | - $actions[] = [ |
|
214 | - 'link' => Helpers::editorUri($file, 1, $label = is_file($file) ? 'open' : 'create'), |
|
215 | - 'label' => $label . ' file', |
|
216 | - ]; |
|
217 | - } |
|
218 | - |
|
219 | - $query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex) . ' ') |
|
220 | - . preg_replace('#\'.*\'|".*"#Us', '', $ex->getMessage()); |
|
221 | - $actions[] = [ |
|
222 | - 'link' => 'https://www.google.com/search?sourceid=tracy&q=' . urlencode($query), |
|
223 | - 'label' => 'search', |
|
224 | - 'external' => true, |
|
225 | - ]; |
|
226 | - |
|
227 | - if ( |
|
228 | - $ex instanceof \ErrorException |
|
229 | - && !empty($ex->skippable) |
|
230 | - && preg_match('#^https?://#', $source = Helpers::getSource()) |
|
231 | - ) { |
|
232 | - $actions[] = [ |
|
233 | - 'link' => $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error', |
|
234 | - 'label' => 'skip error', |
|
235 | - ]; |
|
236 | - } |
|
237 | - return $actions; |
|
238 | - } |
|
239 | - |
|
240 | - |
|
241 | - /** |
|
242 | - * Returns syntax highlighted source code. |
|
243 | - * @param string $file |
|
244 | - * @param int $line |
|
245 | - * @param int $lines |
|
246 | - * @return string|null |
|
247 | - */ |
|
248 | - public static function highlightFile($file, $line, $lines = 15, array $vars = null) |
|
249 | - { |
|
250 | - $source = @file_get_contents($file); // @ file may not exist |
|
251 | - if ($source) { |
|
252 | - $source = static::highlightPhp($source, $line, $lines, $vars); |
|
253 | - if ($editor = Helpers::editorUri($file, $line)) { |
|
254 | - $source = substr_replace($source, ' data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); |
|
255 | - } |
|
256 | - return $source; |
|
257 | - } |
|
258 | - } |
|
259 | - |
|
260 | - |
|
261 | - /** |
|
262 | - * Returns syntax highlighted source code. |
|
263 | - * @param string $source |
|
264 | - * @param int $line |
|
265 | - * @param int $lines |
|
266 | - * @return string |
|
267 | - */ |
|
268 | - public static function highlightPhp($source, $line, $lines = 15, array $vars = null) |
|
269 | - { |
|
270 | - if (function_exists('ini_set')) { |
|
271 | - ini_set('highlight.comment', '#998; font-style: italic'); |
|
272 | - ini_set('highlight.default', '#000'); |
|
273 | - ini_set('highlight.html', '#06B'); |
|
274 | - ini_set('highlight.keyword', '#D24; font-weight: bold'); |
|
275 | - ini_set('highlight.string', '#080'); |
|
276 | - } |
|
277 | - |
|
278 | - $source = str_replace(["\r\n", "\r"], "\n", $source); |
|
279 | - $source = explode("\n", highlight_string($source, true)); |
|
280 | - $out = $source[0]; // <code><span color=highlight.html> |
|
281 | - $source = str_replace('<br />', "\n", $source[1]); |
|
282 | - $out .= static::highlightLine($source, $line, $lines); |
|
283 | - |
|
284 | - if ($vars) { |
|
285 | - $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars) { |
|
286 | - return array_key_exists($m[1], $vars) |
|
287 | - ? '" title="' |
|
288 | - . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], [Dumper::DEPTH => 1])))) |
|
289 | - . $m[0] |
|
290 | - : $m[0]; |
|
291 | - }, $out); |
|
292 | - } |
|
293 | - |
|
294 | - $out = str_replace(' ', ' ', $out); |
|
295 | - return "<pre class='code'><div>$out</div></pre>"; |
|
296 | - } |
|
297 | - |
|
298 | - |
|
299 | - /** |
|
300 | - * Returns highlighted line in HTML code. |
|
301 | - * @return string |
|
302 | - */ |
|
303 | - public static function highlightLine($html, $line, $lines = 15) |
|
304 | - { |
|
305 | - $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html)); |
|
306 | - $out = ''; |
|
307 | - $spans = 1; |
|
308 | - $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3)); |
|
309 | - while (--$i >= 1) { // find last highlighted block |
|
310 | - if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) { |
|
311 | - if ($m[1] !== '</span>') { |
|
312 | - $spans++; |
|
313 | - $out .= $m[1]; |
|
314 | - } |
|
315 | - break; |
|
316 | - } |
|
317 | - } |
|
318 | - |
|
319 | - $source = array_slice($source, $start, $lines, true); |
|
320 | - end($source); |
|
321 | - $numWidth = strlen((string) key($source)); |
|
322 | - |
|
323 | - foreach ($source as $n => $s) { |
|
324 | - $spans += substr_count($s, '<span') - substr_count($s, '</span'); |
|
325 | - $s = str_replace(["\r", "\n"], ['', ''], $s); |
|
326 | - preg_match_all('#<[^>]+>#', $s, $tags); |
|
327 | - if ($n == $line) { |
|
328 | - $out .= sprintf( |
|
329 | - "<span class='highlight'>%{$numWidth}s: %s\n</span>%s", |
|
330 | - $n, |
|
331 | - strip_tags($s), |
|
332 | - implode('', $tags[0]) |
|
333 | - ); |
|
334 | - } else { |
|
335 | - $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s); |
|
336 | - } |
|
337 | - } |
|
338 | - $out .= str_repeat('</span>', $spans) . '</code>'; |
|
339 | - return $out; |
|
340 | - } |
|
341 | - |
|
342 | - |
|
343 | - /** |
|
344 | - * Should a file be collapsed in stack trace? |
|
345 | - * @param string $file |
|
346 | - * @return bool |
|
347 | - */ |
|
348 | - public function isCollapsed($file) |
|
349 | - { |
|
350 | - $file = strtr($file, '\\', '/') . '/'; |
|
351 | - foreach ($this->collapsePaths as $path) { |
|
352 | - $path = strtr($path, '\\', '/') . '/'; |
|
353 | - if (strncmp($file, $path, strlen($path)) === 0) { |
|
354 | - return true; |
|
355 | - } |
|
356 | - } |
|
357 | - return false; |
|
358 | - } |
|
16 | + /** @var string[] */ |
|
17 | + public $info = []; |
|
18 | + |
|
19 | + /** @var string[] paths to be collapsed in stack trace (e.g. core libraries) */ |
|
20 | + public $collapsePaths = []; |
|
21 | + |
|
22 | + /** @var int */ |
|
23 | + public $maxDepth = 3; |
|
24 | + |
|
25 | + /** @var int */ |
|
26 | + public $maxLength = 150; |
|
27 | + |
|
28 | + /** @var string[] */ |
|
29 | + public $keysToHide = ['password', 'passwd', 'pass', 'pwd', 'creditcard', 'credit card', 'cc', 'pin']; |
|
30 | + |
|
31 | + /** @var callable[] */ |
|
32 | + private $panels = []; |
|
33 | + |
|
34 | + /** @var callable[] functions that returns action for exceptions */ |
|
35 | + private $actions = []; |
|
36 | + |
|
37 | + |
|
38 | + public function __construct() |
|
39 | + { |
|
40 | + $this->collapsePaths[] = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy$#', strtr(__DIR__, '\\', '/'), $m) |
|
41 | + ? $m[1] |
|
42 | + : __DIR__; |
|
43 | + } |
|
44 | + |
|
45 | + |
|
46 | + /** |
|
47 | + * Add custom panel. |
|
48 | + * @param callable $panel |
|
49 | + * @return static |
|
50 | + */ |
|
51 | + public function addPanel($panel) |
|
52 | + { |
|
53 | + if (!in_array($panel, $this->panels, true)) { |
|
54 | + $this->panels[] = $panel; |
|
55 | + } |
|
56 | + return $this; |
|
57 | + } |
|
58 | + |
|
59 | + |
|
60 | + /** |
|
61 | + * Add action. |
|
62 | + * @param callable $action |
|
63 | + * @return static |
|
64 | + */ |
|
65 | + public function addAction($action) |
|
66 | + { |
|
67 | + $this->actions[] = $action; |
|
68 | + return $this; |
|
69 | + } |
|
70 | + |
|
71 | + |
|
72 | + /** |
|
73 | + * Renders blue screen. |
|
74 | + * @param \Exception|\Throwable $exception |
|
75 | + * @return void |
|
76 | + */ |
|
77 | + public function render($exception) |
|
78 | + { |
|
79 | + if (Helpers::isAjax() && session_status() === PHP_SESSION_ACTIVE) { |
|
80 | + ob_start(function () {}); |
|
81 | + $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/content.phtml'); |
|
82 | + $contentId = $_SERVER['HTTP_X_TRACY_AJAX']; |
|
83 | + $_SESSION['_tracy']['bluescreen'][$contentId] = ['content' => ob_get_clean(), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
|
84 | + |
|
85 | + } else { |
|
86 | + $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml'); |
|
87 | + } |
|
88 | + } |
|
89 | + |
|
90 | + |
|
91 | + /** |
|
92 | + * Renders blue screen to file (if file exists, it will not be overwritten). |
|
93 | + * @param \Exception|\Throwable $exception |
|
94 | + * @param string $file file path |
|
95 | + * @return void |
|
96 | + */ |
|
97 | + public function renderToFile($exception, $file) |
|
98 | + { |
|
99 | + if ($handle = @fopen($file, 'x')) { |
|
100 | + ob_start(); // double buffer prevents sending HTTP headers in some PHP |
|
101 | + ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); |
|
102 | + $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml', false); |
|
103 | + ob_end_flush(); |
|
104 | + ob_end_clean(); |
|
105 | + fclose($handle); |
|
106 | + } |
|
107 | + } |
|
108 | + |
|
109 | + |
|
110 | + private function renderTemplate($exception, $template, $toScreen = true) |
|
111 | + { |
|
112 | + $messageHtml = preg_replace( |
|
113 | + '#\'\S[^\']*\S\'|"\S[^"]*\S"#U', |
|
114 | + '<i>$0</i>', |
|
115 | + htmlspecialchars((string) $exception->getMessage(), ENT_SUBSTITUTE, 'UTF-8') |
|
116 | + ); |
|
117 | + $info = array_filter($this->info); |
|
118 | + $source = Helpers::getSource(); |
|
119 | + $sourceIsUrl = preg_match('#^https?://#', $source); |
|
120 | + $title = $exception instanceof \ErrorException |
|
121 | + ? Helpers::errorTypeToString($exception->getSeverity()) |
|
122 | + : Helpers::getClass($exception); |
|
123 | + $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? null : error_get_last(); |
|
124 | + |
|
125 | + $keysToHide = array_flip(array_map('strtolower', $this->keysToHide)); |
|
126 | + $dump = function ($v, $k = null) use ($keysToHide) { |
|
127 | + if (is_string($k) && isset($keysToHide[strtolower($k)])) { |
|
128 | + $v = Dumper::HIDDEN_VALUE; |
|
129 | + } |
|
130 | + return Dumper::toHtml($v, [ |
|
131 | + Dumper::DEPTH => $this->maxDepth, |
|
132 | + Dumper::TRUNCATE => $this->maxLength, |
|
133 | + Dumper::LIVE => true, |
|
134 | + Dumper::LOCATION => Dumper::LOCATION_CLASS, |
|
135 | + Dumper::KEYS_TO_HIDE => $this->keysToHide, |
|
136 | + ]); |
|
137 | + }; |
|
138 | + $css = array_map('file_get_contents', array_merge([ |
|
139 | + __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
140 | + ], Debugger::$customCssFiles)); |
|
141 | + $css = preg_replace('#\s+#u', ' ', implode($css)); |
|
142 | + |
|
143 | + $nonce = $toScreen ? Helpers::getNonce() : null; |
|
144 | + $actions = $toScreen ? $this->renderActions($exception) : []; |
|
145 | + |
|
146 | + require $template; |
|
147 | + } |
|
148 | + |
|
149 | + |
|
150 | + /** |
|
151 | + * @return \stdClass[] |
|
152 | + */ |
|
153 | + private function renderPanels($ex) |
|
154 | + { |
|
155 | + $obLevel = ob_get_level(); |
|
156 | + $res = []; |
|
157 | + foreach ($this->panels as $callback) { |
|
158 | + try { |
|
159 | + $panel = call_user_func($callback, $ex); |
|
160 | + if (empty($panel['tab']) || empty($panel['panel'])) { |
|
161 | + continue; |
|
162 | + } |
|
163 | + $res[] = (object) $panel; |
|
164 | + continue; |
|
165 | + } catch (\Exception $e) { |
|
166 | + } catch (\Throwable $e) { |
|
167 | + } |
|
168 | + while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
169 | + ob_end_clean(); |
|
170 | + } |
|
171 | + is_callable($callback, true, $name); |
|
172 | + $res[] = (object) [ |
|
173 | + 'tab' => "Error in panel $name", |
|
174 | + 'panel' => nl2br(Helpers::escapeHtml($e)), |
|
175 | + ]; |
|
176 | + } |
|
177 | + return $res; |
|
178 | + } |
|
179 | + |
|
180 | + |
|
181 | + /** |
|
182 | + * @return array[] |
|
183 | + */ |
|
184 | + private function renderActions($ex) |
|
185 | + { |
|
186 | + $actions = []; |
|
187 | + foreach ($this->actions as $callback) { |
|
188 | + $action = call_user_func($callback, $ex); |
|
189 | + if (!empty($action['link']) && !empty($action['label'])) { |
|
190 | + $actions[] = $action; |
|
191 | + } |
|
192 | + } |
|
193 | + |
|
194 | + if (property_exists($ex, 'tracyAction') && !empty($ex->tracyAction['link']) && !empty($ex->tracyAction['label'])) { |
|
195 | + $actions[] = $ex->tracyAction; |
|
196 | + } |
|
197 | + |
|
198 | + if (preg_match('# ([\'"])(\w{3,}(?:\\\\\w{3,})+)\\1#i', $ex->getMessage(), $m)) { |
|
199 | + $class = $m[2]; |
|
200 | + if ( |
|
201 | + !class_exists($class) && !interface_exists($class) && !trait_exists($class) |
|
202 | + && ($file = Helpers::guessClassFile($class)) && !is_file($file) |
|
203 | + ) { |
|
204 | + $actions[] = [ |
|
205 | + 'link' => Helpers::editorUri($file, 1, 'create'), |
|
206 | + 'label' => 'create class', |
|
207 | + ]; |
|
208 | + } |
|
209 | + } |
|
210 | + |
|
211 | + if (preg_match('# ([\'"])((?:/|[a-z]:[/\\\\])\w[^\'"]+\.\w{2,5})\\1#i', $ex->getMessage(), $m)) { |
|
212 | + $file = $m[2]; |
|
213 | + $actions[] = [ |
|
214 | + 'link' => Helpers::editorUri($file, 1, $label = is_file($file) ? 'open' : 'create'), |
|
215 | + 'label' => $label . ' file', |
|
216 | + ]; |
|
217 | + } |
|
218 | + |
|
219 | + $query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex) . ' ') |
|
220 | + . preg_replace('#\'.*\'|".*"#Us', '', $ex->getMessage()); |
|
221 | + $actions[] = [ |
|
222 | + 'link' => 'https://www.google.com/search?sourceid=tracy&q=' . urlencode($query), |
|
223 | + 'label' => 'search', |
|
224 | + 'external' => true, |
|
225 | + ]; |
|
226 | + |
|
227 | + if ( |
|
228 | + $ex instanceof \ErrorException |
|
229 | + && !empty($ex->skippable) |
|
230 | + && preg_match('#^https?://#', $source = Helpers::getSource()) |
|
231 | + ) { |
|
232 | + $actions[] = [ |
|
233 | + 'link' => $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error', |
|
234 | + 'label' => 'skip error', |
|
235 | + ]; |
|
236 | + } |
|
237 | + return $actions; |
|
238 | + } |
|
239 | + |
|
240 | + |
|
241 | + /** |
|
242 | + * Returns syntax highlighted source code. |
|
243 | + * @param string $file |
|
244 | + * @param int $line |
|
245 | + * @param int $lines |
|
246 | + * @return string|null |
|
247 | + */ |
|
248 | + public static function highlightFile($file, $line, $lines = 15, array $vars = null) |
|
249 | + { |
|
250 | + $source = @file_get_contents($file); // @ file may not exist |
|
251 | + if ($source) { |
|
252 | + $source = static::highlightPhp($source, $line, $lines, $vars); |
|
253 | + if ($editor = Helpers::editorUri($file, $line)) { |
|
254 | + $source = substr_replace($source, ' data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); |
|
255 | + } |
|
256 | + return $source; |
|
257 | + } |
|
258 | + } |
|
259 | + |
|
260 | + |
|
261 | + /** |
|
262 | + * Returns syntax highlighted source code. |
|
263 | + * @param string $source |
|
264 | + * @param int $line |
|
265 | + * @param int $lines |
|
266 | + * @return string |
|
267 | + */ |
|
268 | + public static function highlightPhp($source, $line, $lines = 15, array $vars = null) |
|
269 | + { |
|
270 | + if (function_exists('ini_set')) { |
|
271 | + ini_set('highlight.comment', '#998; font-style: italic'); |
|
272 | + ini_set('highlight.default', '#000'); |
|
273 | + ini_set('highlight.html', '#06B'); |
|
274 | + ini_set('highlight.keyword', '#D24; font-weight: bold'); |
|
275 | + ini_set('highlight.string', '#080'); |
|
276 | + } |
|
277 | + |
|
278 | + $source = str_replace(["\r\n", "\r"], "\n", $source); |
|
279 | + $source = explode("\n", highlight_string($source, true)); |
|
280 | + $out = $source[0]; // <code><span color=highlight.html> |
|
281 | + $source = str_replace('<br />', "\n", $source[1]); |
|
282 | + $out .= static::highlightLine($source, $line, $lines); |
|
283 | + |
|
284 | + if ($vars) { |
|
285 | + $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars) { |
|
286 | + return array_key_exists($m[1], $vars) |
|
287 | + ? '" title="' |
|
288 | + . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], [Dumper::DEPTH => 1])))) |
|
289 | + . $m[0] |
|
290 | + : $m[0]; |
|
291 | + }, $out); |
|
292 | + } |
|
293 | + |
|
294 | + $out = str_replace(' ', ' ', $out); |
|
295 | + return "<pre class='code'><div>$out</div></pre>"; |
|
296 | + } |
|
297 | + |
|
298 | + |
|
299 | + /** |
|
300 | + * Returns highlighted line in HTML code. |
|
301 | + * @return string |
|
302 | + */ |
|
303 | + public static function highlightLine($html, $line, $lines = 15) |
|
304 | + { |
|
305 | + $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html)); |
|
306 | + $out = ''; |
|
307 | + $spans = 1; |
|
308 | + $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3)); |
|
309 | + while (--$i >= 1) { // find last highlighted block |
|
310 | + if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) { |
|
311 | + if ($m[1] !== '</span>') { |
|
312 | + $spans++; |
|
313 | + $out .= $m[1]; |
|
314 | + } |
|
315 | + break; |
|
316 | + } |
|
317 | + } |
|
318 | + |
|
319 | + $source = array_slice($source, $start, $lines, true); |
|
320 | + end($source); |
|
321 | + $numWidth = strlen((string) key($source)); |
|
322 | + |
|
323 | + foreach ($source as $n => $s) { |
|
324 | + $spans += substr_count($s, '<span') - substr_count($s, '</span'); |
|
325 | + $s = str_replace(["\r", "\n"], ['', ''], $s); |
|
326 | + preg_match_all('#<[^>]+>#', $s, $tags); |
|
327 | + if ($n == $line) { |
|
328 | + $out .= sprintf( |
|
329 | + "<span class='highlight'>%{$numWidth}s: %s\n</span>%s", |
|
330 | + $n, |
|
331 | + strip_tags($s), |
|
332 | + implode('', $tags[0]) |
|
333 | + ); |
|
334 | + } else { |
|
335 | + $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s); |
|
336 | + } |
|
337 | + } |
|
338 | + $out .= str_repeat('</span>', $spans) . '</code>'; |
|
339 | + return $out; |
|
340 | + } |
|
341 | + |
|
342 | + |
|
343 | + /** |
|
344 | + * Should a file be collapsed in stack trace? |
|
345 | + * @param string $file |
|
346 | + * @return bool |
|
347 | + */ |
|
348 | + public function isCollapsed($file) |
|
349 | + { |
|
350 | + $file = strtr($file, '\\', '/') . '/'; |
|
351 | + foreach ($this->collapsePaths as $path) { |
|
352 | + $path = strtr($path, '\\', '/') . '/'; |
|
353 | + if (strncmp($file, $path, strlen($path)) === 0) { |
|
354 | + return true; |
|
355 | + } |
|
356 | + } |
|
357 | + return false; |
|
358 | + } |
|
359 | 359 | } |
@@ -77,13 +77,13 @@ discard block |
||
77 | 77 | public function render($exception) |
78 | 78 | { |
79 | 79 | if (Helpers::isAjax() && session_status() === PHP_SESSION_ACTIVE) { |
80 | - ob_start(function () {}); |
|
81 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/content.phtml'); |
|
80 | + ob_start(function(){}); |
|
81 | + $this->renderTemplate($exception, __DIR__.'/assets/BlueScreen/content.phtml'); |
|
82 | 82 | $contentId = $_SERVER['HTTP_X_TRACY_AJAX']; |
83 | 83 | $_SESSION['_tracy']['bluescreen'][$contentId] = ['content' => ob_get_clean(), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; |
84 | 84 | |
85 | 85 | } else { |
86 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml'); |
|
86 | + $this->renderTemplate($exception, __DIR__.'/assets/BlueScreen/page.phtml'); |
|
87 | 87 | } |
88 | 88 | } |
89 | 89 | |
@@ -98,8 +98,8 @@ discard block |
||
98 | 98 | { |
99 | 99 | if ($handle = @fopen($file, 'x')) { |
100 | 100 | ob_start(); // double buffer prevents sending HTTP headers in some PHP |
101 | - ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); |
|
102 | - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml', false); |
|
101 | + ob_start(function($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); |
|
102 | + $this->renderTemplate($exception, __DIR__.'/assets/BlueScreen/page.phtml', false); |
|
103 | 103 | ob_end_flush(); |
104 | 104 | ob_end_clean(); |
105 | 105 | fclose($handle); |
@@ -123,7 +123,7 @@ discard block |
||
123 | 123 | $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? null : error_get_last(); |
124 | 124 | |
125 | 125 | $keysToHide = array_flip(array_map('strtolower', $this->keysToHide)); |
126 | - $dump = function ($v, $k = null) use ($keysToHide) { |
|
126 | + $dump = function($v, $k = null) use ($keysToHide) { |
|
127 | 127 | if (is_string($k) && isset($keysToHide[strtolower($k)])) { |
128 | 128 | $v = Dumper::HIDDEN_VALUE; |
129 | 129 | } |
@@ -136,7 +136,7 @@ discard block |
||
136 | 136 | ]); |
137 | 137 | }; |
138 | 138 | $css = array_map('file_get_contents', array_merge([ |
139 | - __DIR__ . '/assets/BlueScreen/bluescreen.css', |
|
139 | + __DIR__.'/assets/BlueScreen/bluescreen.css', |
|
140 | 140 | ], Debugger::$customCssFiles)); |
141 | 141 | $css = preg_replace('#\s+#u', ' ', implode($css)); |
142 | 142 | |
@@ -212,14 +212,14 @@ discard block |
||
212 | 212 | $file = $m[2]; |
213 | 213 | $actions[] = [ |
214 | 214 | 'link' => Helpers::editorUri($file, 1, $label = is_file($file) ? 'open' : 'create'), |
215 | - 'label' => $label . ' file', |
|
215 | + 'label' => $label.' file', |
|
216 | 216 | ]; |
217 | 217 | } |
218 | 218 | |
219 | - $query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex) . ' ') |
|
219 | + $query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex).' ') |
|
220 | 220 | . preg_replace('#\'.*\'|".*"#Us', '', $ex->getMessage()); |
221 | 221 | $actions[] = [ |
222 | - 'link' => 'https://www.google.com/search?sourceid=tracy&q=' . urlencode($query), |
|
222 | + 'link' => 'https://www.google.com/search?sourceid=tracy&q='.urlencode($query), |
|
223 | 223 | 'label' => 'search', |
224 | 224 | 'external' => true, |
225 | 225 | ]; |
@@ -230,7 +230,7 @@ discard block |
||
230 | 230 | && preg_match('#^https?://#', $source = Helpers::getSource()) |
231 | 231 | ) { |
232 | 232 | $actions[] = [ |
233 | - 'link' => $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error', |
|
233 | + 'link' => $source.(strpos($source, '?') ? '&' : '?').'_tracy_skip_error', |
|
234 | 234 | 'label' => 'skip error', |
235 | 235 | ]; |
236 | 236 | } |
@@ -251,7 +251,7 @@ discard block |
||
251 | 251 | if ($source) { |
252 | 252 | $source = static::highlightPhp($source, $line, $lines, $vars); |
253 | 253 | if ($editor = Helpers::editorUri($file, $line)) { |
254 | - $source = substr_replace($source, ' data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); |
|
254 | + $source = substr_replace($source, ' data-tracy-href="'.Helpers::escapeHtml($editor).'"', 4, 0); |
|
255 | 255 | } |
256 | 256 | return $source; |
257 | 257 | } |
@@ -282,7 +282,7 @@ discard block |
||
282 | 282 | $out .= static::highlightLine($source, $line, $lines); |
283 | 283 | |
284 | 284 | if ($vars) { |
285 | - $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars) { |
|
285 | + $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function($m) use ($vars) { |
|
286 | 286 | return array_key_exists($m[1], $vars) |
287 | 287 | ? '" title="' |
288 | 288 | . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], [Dumper::DEPTH => 1])))) |
@@ -302,7 +302,7 @@ discard block |
||
302 | 302 | */ |
303 | 303 | public static function highlightLine($html, $line, $lines = 15) |
304 | 304 | { |
305 | - $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html)); |
|
305 | + $source = explode("\n", "\n".str_replace("\r\n", "\n", $html)); |
|
306 | 306 | $out = ''; |
307 | 307 | $spans = 1; |
308 | 308 | $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3)); |
@@ -335,7 +335,7 @@ discard block |
||
335 | 335 | $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s); |
336 | 336 | } |
337 | 337 | } |
338 | - $out .= str_repeat('</span>', $spans) . '</code>'; |
|
338 | + $out .= str_repeat('</span>', $spans).'</code>'; |
|
339 | 339 | return $out; |
340 | 340 | } |
341 | 341 | |
@@ -347,9 +347,9 @@ discard block |
||
347 | 347 | */ |
348 | 348 | public function isCollapsed($file) |
349 | 349 | { |
350 | - $file = strtr($file, '\\', '/') . '/'; |
|
350 | + $file = strtr($file, '\\', '/').'/'; |
|
351 | 351 | foreach ($this->collapsePaths as $path) { |
352 | - $path = strtr($path, '\\', '/') . '/'; |
|
352 | + $path = strtr($path, '\\', '/').'/'; |
|
353 | 353 | if (strncmp($file, $path, strlen($path)) === 0) { |
354 | 354 | return true; |
355 | 355 | } |
@@ -123,7 +123,7 @@ discard block |
||
123 | 123 | $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? null : error_get_last(); |
124 | 124 | |
125 | 125 | $keysToHide = array_flip(array_map('strtolower', $this->keysToHide)); |
126 | - $dump = function ($v, $k = null) use ($keysToHide) { |
|
126 | + $dump = function ($v, $k = null) use ($keysToHide){ |
|
127 | 127 | if (is_string($k) && isset($keysToHide[strtolower($k)])) { |
128 | 128 | $v = Dumper::HIDDEN_VALUE; |
129 | 129 | } |
@@ -165,7 +165,8 @@ discard block |
||
165 | 165 | } catch (\Exception $e) { |
166 | 166 | } catch (\Throwable $e) { |
167 | 167 | } |
168 | - while (ob_get_level() > $obLevel) { // restore ob-level if broken |
|
168 | + while (ob_get_level() > $obLevel) { |
|
169 | +// restore ob-level if broken |
|
169 | 170 | ob_end_clean(); |
170 | 171 | } |
171 | 172 | is_callable($callback, true, $name); |
@@ -282,7 +283,7 @@ discard block |
||
282 | 283 | $out .= static::highlightLine($source, $line, $lines); |
283 | 284 | |
284 | 285 | if ($vars) { |
285 | - $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars) { |
|
286 | + $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars){ |
|
286 | 287 | return array_key_exists($m[1], $vars) |
287 | 288 | ? '" title="' |
288 | 289 | . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], [Dumper::DEPTH => 1])))) |
@@ -306,7 +307,8 @@ discard block |
||
306 | 307 | $out = ''; |
307 | 308 | $spans = 1; |
308 | 309 | $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3)); |
309 | - while (--$i >= 1) { // find last highlighted block |
|
310 | + while (--$i >= 1) { |
|
311 | +// find last highlighted block |
|
310 | 312 | if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) { |
311 | 313 | if ($m[1] !== '</span>') { |
312 | 314 | $spans++; |
@@ -369,7 +369,7 @@ discard block |
||
369 | 369 | |
370 | 370 | /** |
371 | 371 | * Handler to catch warnings and notices. |
372 | - * @return bool|null false to call normal error handler, null otherwise |
|
372 | + * @return false|null false to call normal error handler, null otherwise |
|
373 | 373 | * @throws ErrorException |
374 | 374 | * @internal |
375 | 375 | */ |
@@ -437,6 +437,9 @@ discard block |
||
437 | 437 | } |
438 | 438 | |
439 | 439 | |
440 | + /** |
|
441 | + * @param boolean $errorOccurred |
|
442 | + */ |
|
440 | 443 | private static function removeOutputBuffers($errorOccurred) |
441 | 444 | { |
442 | 445 | while (ob_get_level() > self::$obLevel) { |
@@ -597,7 +600,7 @@ discard block |
||
597 | 600 | /** |
598 | 601 | * Logs message or exception. |
599 | 602 | * @param mixed $message |
600 | - * @return mixed |
|
603 | + * @return string |
|
601 | 604 | */ |
602 | 605 | public static function log($message, $priority = ILogger::INFO) |
603 | 606 | { |
@@ -15,629 +15,629 @@ |
||
15 | 15 | */ |
16 | 16 | class Debugger |
17 | 17 | { |
18 | - const VERSION = '2.5.4'; |
|
18 | + const VERSION = '2.5.4'; |
|
19 | 19 | |
20 | - /** server modes for Debugger::enable() */ |
|
21 | - const |
|
22 | - DEVELOPMENT = false, |
|
23 | - PRODUCTION = true, |
|
24 | - DETECT = null; |
|
20 | + /** server modes for Debugger::enable() */ |
|
21 | + const |
|
22 | + DEVELOPMENT = false, |
|
23 | + PRODUCTION = true, |
|
24 | + DETECT = null; |
|
25 | 25 | |
26 | - const COOKIE_SECRET = 'tracy-debug'; |
|
26 | + const COOKIE_SECRET = 'tracy-debug'; |
|
27 | 27 | |
28 | - /** @var bool in production mode is suppressed any debugging output */ |
|
29 | - public static $productionMode = self::DETECT; |
|
28 | + /** @var bool in production mode is suppressed any debugging output */ |
|
29 | + public static $productionMode = self::DETECT; |
|
30 | 30 | |
31 | - /** @var bool whether to display debug bar in development mode */ |
|
32 | - public static $showBar = true; |
|
31 | + /** @var bool whether to display debug bar in development mode */ |
|
32 | + public static $showBar = true; |
|
33 | 33 | |
34 | - /** @var bool whether to send data to FireLogger in development mode */ |
|
35 | - public static $showFireLogger = true; |
|
34 | + /** @var bool whether to send data to FireLogger in development mode */ |
|
35 | + public static $showFireLogger = true; |
|
36 | 36 | |
37 | - /** @var bool */ |
|
38 | - private static $enabled = false; |
|
37 | + /** @var bool */ |
|
38 | + private static $enabled = false; |
|
39 | 39 | |
40 | - /** @var string|null reserved memory; also prevents double rendering */ |
|
41 | - private static $reserved; |
|
40 | + /** @var string|null reserved memory; also prevents double rendering */ |
|
41 | + private static $reserved; |
|
42 | 42 | |
43 | - /** @var int initial output buffer level */ |
|
44 | - private static $obLevel; |
|
43 | + /** @var int initial output buffer level */ |
|
44 | + private static $obLevel; |
|
45 | 45 | |
46 | - /********************* errors and exceptions reporting ****************d*g**/ |
|
46 | + /********************* errors and exceptions reporting ****************d*g**/ |
|
47 | 47 | |
48 | - /** @var bool|int determines whether any error will cause immediate death in development mode; if integer that it's matched against error severity */ |
|
49 | - public static $strictMode = false; |
|
48 | + /** @var bool|int determines whether any error will cause immediate death in development mode; if integer that it's matched against error severity */ |
|
49 | + public static $strictMode = false; |
|
50 | 50 | |
51 | - /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ |
|
52 | - public static $scream = false; |
|
51 | + /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ |
|
52 | + public static $scream = false; |
|
53 | 53 | |
54 | - /** @var callable[] functions that are automatically called after fatal error */ |
|
55 | - public static $onFatalError = []; |
|
54 | + /** @var callable[] functions that are automatically called after fatal error */ |
|
55 | + public static $onFatalError = []; |
|
56 | 56 | |
57 | - /********************* Debugger::dump() ****************d*g**/ |
|
57 | + /********************* Debugger::dump() ****************d*g**/ |
|
58 | 58 | |
59 | - /** @var int how many nested levels of array/object properties display by dump() */ |
|
60 | - public static $maxDepth = 3; |
|
59 | + /** @var int how many nested levels of array/object properties display by dump() */ |
|
60 | + public static $maxDepth = 3; |
|
61 | 61 | |
62 | - /** @var int how long strings display by dump() */ |
|
63 | - public static $maxLength = 150; |
|
62 | + /** @var int how long strings display by dump() */ |
|
63 | + public static $maxLength = 150; |
|
64 | 64 | |
65 | - /** @var bool display location by dump()? */ |
|
66 | - public static $showLocation = false; |
|
65 | + /** @var bool display location by dump()? */ |
|
66 | + public static $showLocation = false; |
|
67 | 67 | |
68 | - /** @deprecated */ |
|
69 | - public static $maxLen = 150; |
|
68 | + /** @deprecated */ |
|
69 | + public static $maxLen = 150; |
|
70 | 70 | |
71 | - /********************* logging ****************d*g**/ |
|
71 | + /********************* logging ****************d*g**/ |
|
72 | 72 | |
73 | - /** @var string|null name of the directory where errors should be logged */ |
|
74 | - public static $logDirectory; |
|
73 | + /** @var string|null name of the directory where errors should be logged */ |
|
74 | + public static $logDirectory; |
|
75 | 75 | |
76 | - /** @var int log bluescreen in production mode for this error severity */ |
|
77 | - public static $logSeverity = 0; |
|
76 | + /** @var int log bluescreen in production mode for this error severity */ |
|
77 | + public static $logSeverity = 0; |
|
78 | 78 | |
79 | - /** @var string|array email(s) to which send error notifications */ |
|
80 | - public static $email; |
|
79 | + /** @var string|array email(s) to which send error notifications */ |
|
80 | + public static $email; |
|
81 | 81 | |
82 | - /** for Debugger::log() and Debugger::fireLog() */ |
|
83 | - const |
|
84 | - DEBUG = ILogger::DEBUG, |
|
85 | - INFO = ILogger::INFO, |
|
86 | - WARNING = ILogger::WARNING, |
|
87 | - ERROR = ILogger::ERROR, |
|
88 | - EXCEPTION = ILogger::EXCEPTION, |
|
89 | - CRITICAL = ILogger::CRITICAL; |
|
82 | + /** for Debugger::log() and Debugger::fireLog() */ |
|
83 | + const |
|
84 | + DEBUG = ILogger::DEBUG, |
|
85 | + INFO = ILogger::INFO, |
|
86 | + WARNING = ILogger::WARNING, |
|
87 | + ERROR = ILogger::ERROR, |
|
88 | + EXCEPTION = ILogger::EXCEPTION, |
|
89 | + CRITICAL = ILogger::CRITICAL; |
|
90 | 90 | |
91 | - /********************* misc ****************d*g**/ |
|
91 | + /********************* misc ****************d*g**/ |
|
92 | 92 | |
93 | - /** @var int timestamp with microseconds of the start of the request */ |
|
94 | - public static $time; |
|
93 | + /** @var int timestamp with microseconds of the start of the request */ |
|
94 | + public static $time; |
|
95 | 95 | |
96 | - /** @var string URI pattern mask to open editor */ |
|
97 | - public static $editor = 'editor://%action/?file=%file&line=%line&search=%search&replace=%replace'; |
|
96 | + /** @var string URI pattern mask to open editor */ |
|
97 | + public static $editor = 'editor://%action/?file=%file&line=%line&search=%search&replace=%replace'; |
|
98 | 98 | |
99 | - /** @var array replacements in path */ |
|
100 | - public static $editorMapping = []; |
|
99 | + /** @var array replacements in path */ |
|
100 | + public static $editorMapping = []; |
|
101 | 101 | |
102 | - /** @var string command to open browser (use 'start ""' in Windows) */ |
|
103 | - public static $browser; |
|
102 | + /** @var string command to open browser (use 'start ""' in Windows) */ |
|
103 | + public static $browser; |
|
104 | 104 | |
105 | - /** @var string custom static error template */ |
|
106 | - public static $errorTemplate; |
|
105 | + /** @var string custom static error template */ |
|
106 | + public static $errorTemplate; |
|
107 | 107 | |
108 | - /** @var string[] */ |
|
109 | - public static $customCssFiles = []; |
|
108 | + /** @var string[] */ |
|
109 | + public static $customCssFiles = []; |
|
110 | 110 | |
111 | - /** @var string[] */ |
|
112 | - public static $customJsFiles = []; |
|
111 | + /** @var string[] */ |
|
112 | + public static $customJsFiles = []; |
|
113 | 113 | |
114 | - /** @var array|null */ |
|
115 | - private static $cpuUsage; |
|
114 | + /** @var array|null */ |
|
115 | + private static $cpuUsage; |
|
116 | 116 | |
117 | - /********************* services ****************d*g**/ |
|
117 | + /********************* services ****************d*g**/ |
|
118 | 118 | |
119 | - /** @var BlueScreen */ |
|
120 | - private static $blueScreen; |
|
119 | + /** @var BlueScreen */ |
|
120 | + private static $blueScreen; |
|
121 | 121 | |
122 | - /** @var Bar */ |
|
123 | - private static $bar; |
|
122 | + /** @var Bar */ |
|
123 | + private static $bar; |
|
124 | 124 | |
125 | - /** @var ILogger */ |
|
126 | - private static $logger; |
|
125 | + /** @var ILogger */ |
|
126 | + private static $logger; |
|
127 | 127 | |
128 | - /** @var ILogger */ |
|
129 | - private static $fireLogger; |
|
128 | + /** @var ILogger */ |
|
129 | + private static $fireLogger; |
|
130 | 130 | |
131 | - |
|
132 | - /** |
|
133 | - * Static class - cannot be instantiated. |
|
134 | - */ |
|
135 | - final public function __construct() |
|
136 | - { |
|
137 | - throw new \LogicException; |
|
138 | - } |
|
139 | - |
|
140 | - |
|
141 | - /** |
|
142 | - * Enables displaying or logging errors and exceptions. |
|
143 | - * @param mixed $mode production, development mode, autodetection or IP address(es) whitelist. |
|
144 | - * @param string $logDirectory error log directory |
|
145 | - * @param string $email administrator email; enables email sending in production mode |
|
146 | - * @return void |
|
147 | - */ |
|
148 | - public static function enable($mode = null, $logDirectory = null, $email = null) |
|
149 | - { |
|
150 | - if ($mode !== null || self::$productionMode === null) { |
|
151 | - self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); |
|
152 | - } |
|
153 | - |
|
154 | - self::$maxLen = &self::$maxLength; |
|
155 | - self::$reserved = str_repeat('t', 30000); |
|
156 | - self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); |
|
157 | - self::$obLevel = ob_get_level(); |
|
158 | - self::$cpuUsage = !self::$productionMode && function_exists('getrusage') ? getrusage() : null; |
|
159 | - |
|
160 | - // logging configuration |
|
161 | - if ($email !== null) { |
|
162 | - self::$email = $email; |
|
163 | - } |
|
164 | - if ($logDirectory !== null) { |
|
165 | - self::$logDirectory = $logDirectory; |
|
166 | - } |
|
167 | - if (self::$logDirectory) { |
|
168 | - if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', self::$logDirectory)) { |
|
169 | - self::exceptionHandler(new \RuntimeException('Logging directory must be absolute path.')); |
|
170 | - self::$logDirectory = null; |
|
171 | - } elseif (!is_dir(self::$logDirectory)) { |
|
172 | - self::exceptionHandler(new \RuntimeException("Logging directory '" . self::$logDirectory . "' is not found.")); |
|
173 | - self::$logDirectory = null; |
|
174 | - } |
|
175 | - } |
|
176 | - |
|
177 | - // php configuration |
|
178 | - if (function_exists('ini_set')) { |
|
179 | - ini_set('display_errors', self::$productionMode ? '0' : '1'); // or 'stderr' |
|
180 | - ini_set('html_errors', '0'); |
|
181 | - ini_set('log_errors', '0'); |
|
182 | - |
|
183 | - } elseif ( |
|
184 | - ini_get('display_errors') != !self::$productionMode // intentionally == |
|
185 | - && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout') |
|
186 | - ) { |
|
187 | - self::exceptionHandler(new \RuntimeException("Unable to set 'display_errors' because function ini_set() is disabled.")); |
|
188 | - } |
|
189 | - error_reporting(E_ALL); |
|
190 | - |
|
191 | - if (self::$enabled) { |
|
192 | - return; |
|
193 | - } |
|
194 | - |
|
195 | - register_shutdown_function([__CLASS__, 'shutdownHandler']); |
|
196 | - set_exception_handler([__CLASS__, 'exceptionHandler']); |
|
197 | - set_error_handler([__CLASS__, 'errorHandler']); |
|
198 | - |
|
199 | - array_map('class_exists', ['Tracy\Bar', 'Tracy\BlueScreen', 'Tracy\DefaultBarPanel', 'Tracy\Dumper', |
|
200 | - 'Tracy\FireLogger', 'Tracy\Helpers', 'Tracy\Logger', ]); |
|
201 | - |
|
202 | - self::dispatch(); |
|
203 | - self::$enabled = true; |
|
204 | - } |
|
205 | - |
|
206 | - |
|
207 | - /** |
|
208 | - * @return void |
|
209 | - */ |
|
210 | - public static function dispatch() |
|
211 | - { |
|
212 | - if (self::$productionMode || PHP_SAPI === 'cli') { |
|
213 | - return; |
|
214 | - |
|
215 | - } elseif (headers_sent($file, $line) || ob_get_length()) { |
|
216 | - throw new \LogicException( |
|
217 | - __METHOD__ . '() called after some output has been sent. ' |
|
218 | - . ($file ? "Output started at $file:$line." : 'Try Tracy\OutputDebugger to find where output started.') |
|
219 | - ); |
|
220 | - |
|
221 | - } elseif (self::$enabled && session_status() !== PHP_SESSION_ACTIVE) { |
|
222 | - ini_set('session.use_cookies', '1'); |
|
223 | - ini_set('session.use_only_cookies', '1'); |
|
224 | - ini_set('session.use_trans_sid', '0'); |
|
225 | - ini_set('session.cookie_path', '/'); |
|
226 | - ini_set('session.cookie_httponly', '1'); |
|
227 | - session_start(); |
|
228 | - } |
|
229 | - |
|
230 | - if (self::getBar()->dispatchAssets()) { |
|
231 | - exit; |
|
232 | - } |
|
233 | - } |
|
234 | - |
|
235 | - |
|
236 | - /** |
|
237 | - * Renders loading <script> |
|
238 | - * @return void |
|
239 | - */ |
|
240 | - public static function renderLoader() |
|
241 | - { |
|
242 | - if (!self::$productionMode) { |
|
243 | - self::getBar()->renderLoader(); |
|
244 | - } |
|
245 | - } |
|
246 | - |
|
247 | - |
|
248 | - /** |
|
249 | - * @return bool |
|
250 | - */ |
|
251 | - public static function isEnabled() |
|
252 | - { |
|
253 | - return self::$enabled; |
|
254 | - } |
|
255 | - |
|
256 | - |
|
257 | - /** |
|
258 | - * Shutdown handler to catch fatal errors and execute of the planned activities. |
|
259 | - * @return void |
|
260 | - * @internal |
|
261 | - */ |
|
262 | - public static function shutdownHandler() |
|
263 | - { |
|
264 | - if (!self::$reserved) { |
|
265 | - return; |
|
266 | - } |
|
267 | - self::$reserved = null; |
|
268 | - |
|
269 | - $error = error_get_last(); |
|
270 | - if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], true)) { |
|
271 | - self::exceptionHandler( |
|
272 | - Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])), |
|
273 | - false |
|
274 | - ); |
|
275 | - |
|
276 | - } elseif (self::$showBar && !self::$productionMode) { |
|
277 | - self::removeOutputBuffers(false); |
|
278 | - self::getBar()->render(); |
|
279 | - } |
|
280 | - } |
|
281 | - |
|
282 | - |
|
283 | - /** |
|
284 | - * Handler to catch uncaught exception. |
|
285 | - * @param \Exception|\Throwable $exception |
|
286 | - * @return void |
|
287 | - * @internal |
|
288 | - */ |
|
289 | - public static function exceptionHandler($exception, $exit = true) |
|
290 | - { |
|
291 | - if (!self::$reserved && $exit) { |
|
292 | - return; |
|
293 | - } |
|
294 | - self::$reserved = null; |
|
295 | - |
|
296 | - if (!headers_sent()) { |
|
297 | - http_response_code(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false ? 503 : 500); |
|
298 | - if (Helpers::isHtmlMode()) { |
|
299 | - header('Content-Type: text/html; charset=UTF-8'); |
|
300 | - } |
|
301 | - } |
|
302 | - |
|
303 | - Helpers::improveException($exception); |
|
304 | - self::removeOutputBuffers(true); |
|
305 | - |
|
306 | - if (self::$productionMode) { |
|
307 | - try { |
|
308 | - self::log($exception, self::EXCEPTION); |
|
309 | - } catch (\Exception $e) { |
|
310 | - } catch (\Throwable $e) { |
|
311 | - } |
|
312 | - |
|
313 | - if (Helpers::isHtmlMode()) { |
|
314 | - $logged = empty($e); |
|
315 | - require self::$errorTemplate ?: __DIR__ . '/assets/Debugger/error.500.phtml'; |
|
316 | - } elseif (PHP_SAPI === 'cli') { |
|
317 | - fwrite(STDERR, 'ERROR: application encountered an error and can not continue. ' |
|
318 | - . (isset($e) ? "Unable to log error.\n" : "Error was logged.\n")); |
|
319 | - } |
|
320 | - |
|
321 | - } elseif (!connection_aborted() && (Helpers::isHtmlMode() || Helpers::isAjax())) { |
|
322 | - self::getBlueScreen()->render($exception); |
|
323 | - if (self::$showBar) { |
|
324 | - self::getBar()->render(); |
|
325 | - } |
|
326 | - |
|
327 | - } else { |
|
328 | - self::fireLog($exception); |
|
329 | - $s = get_class($exception) . ($exception->getMessage() === '' ? '' : ': ' . $exception->getMessage()) |
|
330 | - . ' in ' . $exception->getFile() . ':' . $exception->getLine() |
|
331 | - . "\nStack trace:\n" . $exception->getTraceAsString(); |
|
332 | - try { |
|
333 | - $file = self::log($exception, self::EXCEPTION); |
|
334 | - if ($file && !headers_sent()) { |
|
335 | - header("X-Tracy-Error-Log: $file"); |
|
336 | - } |
|
337 | - echo "$s\n" . ($file ? "(stored in $file)\n" : ''); |
|
338 | - if ($file && self::$browser) { |
|
339 | - exec(self::$browser . ' ' . escapeshellarg($file)); |
|
340 | - } |
|
341 | - } catch (\Exception $e) { |
|
342 | - echo "$s\nUnable to log error: {$e->getMessage()}\n"; |
|
343 | - } catch (\Throwable $e) { |
|
344 | - echo "$s\nUnable to log error: {$e->getMessage()}\n"; |
|
345 | - } |
|
346 | - } |
|
347 | - |
|
348 | - try { |
|
349 | - $e = null; |
|
350 | - foreach (self::$onFatalError as $handler) { |
|
351 | - call_user_func($handler, $exception); |
|
352 | - } |
|
353 | - } catch (\Exception $e) { |
|
354 | - } catch (\Throwable $e) { |
|
355 | - } |
|
356 | - if ($e) { |
|
357 | - try { |
|
358 | - self::log($e, self::EXCEPTION); |
|
359 | - } catch (\Exception $e) { |
|
360 | - } catch (\Throwable $e) { |
|
361 | - } |
|
362 | - } |
|
363 | - |
|
364 | - if ($exit) { |
|
365 | - exit(255); |
|
366 | - } |
|
367 | - } |
|
368 | - |
|
369 | - |
|
370 | - /** |
|
371 | - * Handler to catch warnings and notices. |
|
372 | - * @return bool|null false to call normal error handler, null otherwise |
|
373 | - * @throws ErrorException |
|
374 | - * @internal |
|
375 | - */ |
|
376 | - public static function errorHandler($severity, $message, $file, $line, $context = []) |
|
377 | - { |
|
378 | - if (self::$scream) { |
|
379 | - error_reporting(E_ALL); |
|
380 | - } |
|
381 | - |
|
382 | - if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { |
|
383 | - if (Helpers::findTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), '*::__toString')) { |
|
384 | - $previous = isset($context['e']) && ($context['e'] instanceof \Exception || $context['e'] instanceof \Throwable) ? $context['e'] : null; |
|
385 | - $e = new ErrorException($message, 0, $severity, $file, $line, $previous); |
|
386 | - $e->context = $context; |
|
387 | - self::exceptionHandler($e); |
|
388 | - } |
|
389 | - |
|
390 | - $e = new ErrorException($message, 0, $severity, $file, $line); |
|
391 | - $e->context = $context; |
|
392 | - throw $e; |
|
393 | - |
|
394 | - } elseif (($severity & error_reporting()) !== $severity) { |
|
395 | - return false; // calls normal error handler to fill-in error_get_last() |
|
396 | - |
|
397 | - } elseif (self::$productionMode && ($severity & self::$logSeverity) === $severity) { |
|
398 | - $e = new ErrorException($message, 0, $severity, $file, $line); |
|
399 | - $e->context = $context; |
|
400 | - Helpers::improveException($e); |
|
401 | - try { |
|
402 | - self::log($e, self::ERROR); |
|
403 | - } catch (\Exception $foo) { |
|
404 | - } catch (\Throwable $foo) { |
|
405 | - } |
|
406 | - return null; |
|
407 | - |
|
408 | - } elseif ( |
|
409 | - !self::$productionMode |
|
410 | - && !isset($_GET['_tracy_skip_error']) |
|
411 | - && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity)) |
|
412 | - ) { |
|
413 | - $e = new ErrorException($message, 0, $severity, $file, $line); |
|
414 | - $e->context = $context; |
|
415 | - $e->skippable = true; |
|
416 | - self::exceptionHandler($e); |
|
417 | - } |
|
418 | - |
|
419 | - $message = 'PHP ' . Helpers::errorTypeToString($severity) . ': ' . Helpers::improveError($message, $context); |
|
420 | - $count = &self::getBar()->getPanel('Tracy:errors')->data["$file|$line|$message"]; |
|
421 | - |
|
422 | - if ($count++) { // repeated error |
|
423 | - return null; |
|
424 | - |
|
425 | - } elseif (self::$productionMode) { |
|
426 | - try { |
|
427 | - self::log("$message in $file:$line", self::ERROR); |
|
428 | - } catch (\Exception $foo) { |
|
429 | - } catch (\Throwable $foo) { |
|
430 | - } |
|
431 | - return null; |
|
432 | - |
|
433 | - } else { |
|
434 | - self::fireLog(new ErrorException($message, 0, $severity, $file, $line)); |
|
435 | - return Helpers::isHtmlMode() || Helpers::isAjax() ? null : false; // false calls normal error handler |
|
436 | - } |
|
437 | - } |
|
438 | - |
|
439 | - |
|
440 | - private static function removeOutputBuffers($errorOccurred) |
|
441 | - { |
|
442 | - while (ob_get_level() > self::$obLevel) { |
|
443 | - $status = ob_get_status(); |
|
444 | - if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'], true)) { |
|
445 | - break; |
|
446 | - } |
|
447 | - $fnc = $status['chunk_size'] || !$errorOccurred ? 'ob_end_flush' : 'ob_end_clean'; |
|
448 | - if (!@$fnc()) { // @ may be not removable |
|
449 | - break; |
|
450 | - } |
|
451 | - } |
|
452 | - } |
|
453 | - |
|
454 | - |
|
455 | - /********************* services ****************d*g**/ |
|
456 | - |
|
457 | - |
|
458 | - /** |
|
459 | - * @return BlueScreen |
|
460 | - */ |
|
461 | - public static function getBlueScreen() |
|
462 | - { |
|
463 | - if (!self::$blueScreen) { |
|
464 | - self::$blueScreen = new BlueScreen; |
|
465 | - self::$blueScreen->info = [ |
|
466 | - 'PHP ' . PHP_VERSION, |
|
467 | - isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, |
|
468 | - 'Tracy ' . self::VERSION, |
|
469 | - ]; |
|
470 | - } |
|
471 | - return self::$blueScreen; |
|
472 | - } |
|
473 | - |
|
474 | - |
|
475 | - /** |
|
476 | - * @return Bar |
|
477 | - */ |
|
478 | - public static function getBar() |
|
479 | - { |
|
480 | - if (!self::$bar) { |
|
481 | - self::$bar = new Bar; |
|
482 | - self::$bar->addPanel($info = new DefaultBarPanel('info'), 'Tracy:info'); |
|
483 | - $info->cpuUsage = self::$cpuUsage; |
|
484 | - self::$bar->addPanel(new DefaultBarPanel('errors'), 'Tracy:errors'); // filled by errorHandler() |
|
485 | - } |
|
486 | - return self::$bar; |
|
487 | - } |
|
488 | - |
|
489 | - |
|
490 | - /** |
|
491 | - * @return void |
|
492 | - */ |
|
493 | - public static function setLogger(ILogger $logger) |
|
494 | - { |
|
495 | - self::$logger = $logger; |
|
496 | - } |
|
497 | - |
|
498 | - |
|
499 | - /** |
|
500 | - * @return ILogger |
|
501 | - */ |
|
502 | - public static function getLogger() |
|
503 | - { |
|
504 | - if (!self::$logger) { |
|
505 | - self::$logger = new Logger(self::$logDirectory, self::$email, self::getBlueScreen()); |
|
506 | - self::$logger->directory = &self::$logDirectory; // back compatiblity |
|
507 | - self::$logger->email = &self::$email; |
|
508 | - } |
|
509 | - return self::$logger; |
|
510 | - } |
|
511 | - |
|
512 | - |
|
513 | - /** |
|
514 | - * @return ILogger |
|
515 | - */ |
|
516 | - public static function getFireLogger() |
|
517 | - { |
|
518 | - if (!self::$fireLogger) { |
|
519 | - self::$fireLogger = new FireLogger; |
|
520 | - } |
|
521 | - return self::$fireLogger; |
|
522 | - } |
|
523 | - |
|
524 | - |
|
525 | - /********************* useful tools ****************d*g**/ |
|
526 | - |
|
527 | - |
|
528 | - /** |
|
529 | - * Dumps information about a variable in readable format. |
|
530 | - * @tracySkipLocation |
|
531 | - * @param mixed $var variable to dump |
|
532 | - * @param bool $return return output instead of printing it? (bypasses $productionMode) |
|
533 | - * @return mixed variable itself or dump |
|
534 | - */ |
|
535 | - public static function dump($var, $return = false) |
|
536 | - { |
|
537 | - if ($return) { |
|
538 | - ob_start(function () {}); |
|
539 | - Dumper::dump($var, [ |
|
540 | - Dumper::DEPTH => self::$maxDepth, |
|
541 | - Dumper::TRUNCATE => self::$maxLength, |
|
542 | - ]); |
|
543 | - return ob_get_clean(); |
|
544 | - |
|
545 | - } elseif (!self::$productionMode) { |
|
546 | - Dumper::dump($var, [ |
|
547 | - Dumper::DEPTH => self::$maxDepth, |
|
548 | - Dumper::TRUNCATE => self::$maxLength, |
|
549 | - Dumper::LOCATION => self::$showLocation, |
|
550 | - ]); |
|
551 | - } |
|
552 | - |
|
553 | - return $var; |
|
554 | - } |
|
555 | - |
|
556 | - |
|
557 | - /** |
|
558 | - * Starts/stops stopwatch. |
|
559 | - * @param string $name |
|
560 | - * @return float elapsed seconds |
|
561 | - */ |
|
562 | - public static function timer($name = null) |
|
563 | - { |
|
564 | - static $time = []; |
|
565 | - $now = microtime(true); |
|
566 | - $delta = isset($time[$name]) ? $now - $time[$name] : 0; |
|
567 | - $time[$name] = $now; |
|
568 | - return $delta; |
|
569 | - } |
|
570 | - |
|
571 | - |
|
572 | - /** |
|
573 | - * Dumps information about a variable in Tracy Debug Bar. |
|
574 | - * @tracySkipLocation |
|
575 | - * @param mixed $var |
|
576 | - * @param string $title |
|
577 | - * @param array $options |
|
578 | - * @return mixed variable itself |
|
579 | - */ |
|
580 | - public static function barDump($var, $title = null, array $options = null) |
|
581 | - { |
|
582 | - if (!self::$productionMode) { |
|
583 | - static $panel; |
|
584 | - if (!$panel) { |
|
585 | - self::getBar()->addPanel($panel = new DefaultBarPanel('dumps'), 'Tracy:dumps'); |
|
586 | - } |
|
587 | - $panel->data[] = ['title' => $title, 'dump' => Dumper::toHtml($var, (array) $options + [ |
|
588 | - Dumper::DEPTH => self::$maxDepth, |
|
589 | - Dumper::TRUNCATE => self::$maxLength, |
|
590 | - Dumper::LOCATION => self::$showLocation ?: Dumper::LOCATION_CLASS | Dumper::LOCATION_SOURCE, |
|
591 | - ])]; |
|
592 | - } |
|
593 | - return $var; |
|
594 | - } |
|
595 | - |
|
596 | - |
|
597 | - /** |
|
598 | - * Logs message or exception. |
|
599 | - * @param mixed $message |
|
600 | - * @return mixed |
|
601 | - */ |
|
602 | - public static function log($message, $priority = ILogger::INFO) |
|
603 | - { |
|
604 | - return self::getLogger()->log($message, $priority); |
|
605 | - } |
|
606 | - |
|
607 | - |
|
608 | - /** |
|
609 | - * Sends message to FireLogger console. |
|
610 | - * @param mixed $message |
|
611 | - * @return bool was successful? |
|
612 | - */ |
|
613 | - public static function fireLog($message) |
|
614 | - { |
|
615 | - if (!self::$productionMode && self::$showFireLogger) { |
|
616 | - return self::getFireLogger()->log($message); |
|
617 | - } |
|
618 | - } |
|
619 | - |
|
620 | - |
|
621 | - /** |
|
622 | - * Detects debug mode by IP address. |
|
623 | - * @param string|array $list IP addresses or computer names whitelist detection |
|
624 | - * @return bool |
|
625 | - */ |
|
626 | - public static function detectDebugMode($list = null) |
|
627 | - { |
|
628 | - $addr = isset($_SERVER['REMOTE_ADDR']) |
|
629 | - ? $_SERVER['REMOTE_ADDR'] |
|
630 | - : php_uname('n'); |
|
631 | - $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET]) |
|
632 | - ? $_COOKIE[self::COOKIE_SECRET] |
|
633 | - : null; |
|
634 | - $list = is_string($list) |
|
635 | - ? preg_split('#[,\s]+#', $list) |
|
636 | - : (array) $list; |
|
637 | - if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) { |
|
638 | - $list[] = '127.0.0.1'; |
|
639 | - $list[] = '::1'; |
|
640 | - } |
|
641 | - return in_array($addr, $list, true) || in_array("$secret@$addr", $list, true); |
|
642 | - } |
|
131 | + |
|
132 | + /** |
|
133 | + * Static class - cannot be instantiated. |
|
134 | + */ |
|
135 | + final public function __construct() |
|
136 | + { |
|
137 | + throw new \LogicException; |
|
138 | + } |
|
139 | + |
|
140 | + |
|
141 | + /** |
|
142 | + * Enables displaying or logging errors and exceptions. |
|
143 | + * @param mixed $mode production, development mode, autodetection or IP address(es) whitelist. |
|
144 | + * @param string $logDirectory error log directory |
|
145 | + * @param string $email administrator email; enables email sending in production mode |
|
146 | + * @return void |
|
147 | + */ |
|
148 | + public static function enable($mode = null, $logDirectory = null, $email = null) |
|
149 | + { |
|
150 | + if ($mode !== null || self::$productionMode === null) { |
|
151 | + self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); |
|
152 | + } |
|
153 | + |
|
154 | + self::$maxLen = &self::$maxLength; |
|
155 | + self::$reserved = str_repeat('t', 30000); |
|
156 | + self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); |
|
157 | + self::$obLevel = ob_get_level(); |
|
158 | + self::$cpuUsage = !self::$productionMode && function_exists('getrusage') ? getrusage() : null; |
|
159 | + |
|
160 | + // logging configuration |
|
161 | + if ($email !== null) { |
|
162 | + self::$email = $email; |
|
163 | + } |
|
164 | + if ($logDirectory !== null) { |
|
165 | + self::$logDirectory = $logDirectory; |
|
166 | + } |
|
167 | + if (self::$logDirectory) { |
|
168 | + if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', self::$logDirectory)) { |
|
169 | + self::exceptionHandler(new \RuntimeException('Logging directory must be absolute path.')); |
|
170 | + self::$logDirectory = null; |
|
171 | + } elseif (!is_dir(self::$logDirectory)) { |
|
172 | + self::exceptionHandler(new \RuntimeException("Logging directory '" . self::$logDirectory . "' is not found.")); |
|
173 | + self::$logDirectory = null; |
|
174 | + } |
|
175 | + } |
|
176 | + |
|
177 | + // php configuration |
|
178 | + if (function_exists('ini_set')) { |
|
179 | + ini_set('display_errors', self::$productionMode ? '0' : '1'); // or 'stderr' |
|
180 | + ini_set('html_errors', '0'); |
|
181 | + ini_set('log_errors', '0'); |
|
182 | + |
|
183 | + } elseif ( |
|
184 | + ini_get('display_errors') != !self::$productionMode // intentionally == |
|
185 | + && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout') |
|
186 | + ) { |
|
187 | + self::exceptionHandler(new \RuntimeException("Unable to set 'display_errors' because function ini_set() is disabled.")); |
|
188 | + } |
|
189 | + error_reporting(E_ALL); |
|
190 | + |
|
191 | + if (self::$enabled) { |
|
192 | + return; |
|
193 | + } |
|
194 | + |
|
195 | + register_shutdown_function([__CLASS__, 'shutdownHandler']); |
|
196 | + set_exception_handler([__CLASS__, 'exceptionHandler']); |
|
197 | + set_error_handler([__CLASS__, 'errorHandler']); |
|
198 | + |
|
199 | + array_map('class_exists', ['Tracy\Bar', 'Tracy\BlueScreen', 'Tracy\DefaultBarPanel', 'Tracy\Dumper', |
|
200 | + 'Tracy\FireLogger', 'Tracy\Helpers', 'Tracy\Logger', ]); |
|
201 | + |
|
202 | + self::dispatch(); |
|
203 | + self::$enabled = true; |
|
204 | + } |
|
205 | + |
|
206 | + |
|
207 | + /** |
|
208 | + * @return void |
|
209 | + */ |
|
210 | + public static function dispatch() |
|
211 | + { |
|
212 | + if (self::$productionMode || PHP_SAPI === 'cli') { |
|
213 | + return; |
|
214 | + |
|
215 | + } elseif (headers_sent($file, $line) || ob_get_length()) { |
|
216 | + throw new \LogicException( |
|
217 | + __METHOD__ . '() called after some output has been sent. ' |
|
218 | + . ($file ? "Output started at $file:$line." : 'Try Tracy\OutputDebugger to find where output started.') |
|
219 | + ); |
|
220 | + |
|
221 | + } elseif (self::$enabled && session_status() !== PHP_SESSION_ACTIVE) { |
|
222 | + ini_set('session.use_cookies', '1'); |
|
223 | + ini_set('session.use_only_cookies', '1'); |
|
224 | + ini_set('session.use_trans_sid', '0'); |
|
225 | + ini_set('session.cookie_path', '/'); |
|
226 | + ini_set('session.cookie_httponly', '1'); |
|
227 | + session_start(); |
|
228 | + } |
|
229 | + |
|
230 | + if (self::getBar()->dispatchAssets()) { |
|
231 | + exit; |
|
232 | + } |
|
233 | + } |
|
234 | + |
|
235 | + |
|
236 | + /** |
|
237 | + * Renders loading <script> |
|
238 | + * @return void |
|
239 | + */ |
|
240 | + public static function renderLoader() |
|
241 | + { |
|
242 | + if (!self::$productionMode) { |
|
243 | + self::getBar()->renderLoader(); |
|
244 | + } |
|
245 | + } |
|
246 | + |
|
247 | + |
|
248 | + /** |
|
249 | + * @return bool |
|
250 | + */ |
|
251 | + public static function isEnabled() |
|
252 | + { |
|
253 | + return self::$enabled; |
|
254 | + } |
|
255 | + |
|
256 | + |
|
257 | + /** |
|
258 | + * Shutdown handler to catch fatal errors and execute of the planned activities. |
|
259 | + * @return void |
|
260 | + * @internal |
|
261 | + */ |
|
262 | + public static function shutdownHandler() |
|
263 | + { |
|
264 | + if (!self::$reserved) { |
|
265 | + return; |
|
266 | + } |
|
267 | + self::$reserved = null; |
|
268 | + |
|
269 | + $error = error_get_last(); |
|
270 | + if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], true)) { |
|
271 | + self::exceptionHandler( |
|
272 | + Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])), |
|
273 | + false |
|
274 | + ); |
|
275 | + |
|
276 | + } elseif (self::$showBar && !self::$productionMode) { |
|
277 | + self::removeOutputBuffers(false); |
|
278 | + self::getBar()->render(); |
|
279 | + } |
|
280 | + } |
|
281 | + |
|
282 | + |
|
283 | + /** |
|
284 | + * Handler to catch uncaught exception. |
|
285 | + * @param \Exception|\Throwable $exception |
|
286 | + * @return void |
|
287 | + * @internal |
|
288 | + */ |
|
289 | + public static function exceptionHandler($exception, $exit = true) |
|
290 | + { |
|
291 | + if (!self::$reserved && $exit) { |
|
292 | + return; |
|
293 | + } |
|
294 | + self::$reserved = null; |
|
295 | + |
|
296 | + if (!headers_sent()) { |
|
297 | + http_response_code(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false ? 503 : 500); |
|
298 | + if (Helpers::isHtmlMode()) { |
|
299 | + header('Content-Type: text/html; charset=UTF-8'); |
|
300 | + } |
|
301 | + } |
|
302 | + |
|
303 | + Helpers::improveException($exception); |
|
304 | + self::removeOutputBuffers(true); |
|
305 | + |
|
306 | + if (self::$productionMode) { |
|
307 | + try { |
|
308 | + self::log($exception, self::EXCEPTION); |
|
309 | + } catch (\Exception $e) { |
|
310 | + } catch (\Throwable $e) { |
|
311 | + } |
|
312 | + |
|
313 | + if (Helpers::isHtmlMode()) { |
|
314 | + $logged = empty($e); |
|
315 | + require self::$errorTemplate ?: __DIR__ . '/assets/Debugger/error.500.phtml'; |
|
316 | + } elseif (PHP_SAPI === 'cli') { |
|
317 | + fwrite(STDERR, 'ERROR: application encountered an error and can not continue. ' |
|
318 | + . (isset($e) ? "Unable to log error.\n" : "Error was logged.\n")); |
|
319 | + } |
|
320 | + |
|
321 | + } elseif (!connection_aborted() && (Helpers::isHtmlMode() || Helpers::isAjax())) { |
|
322 | + self::getBlueScreen()->render($exception); |
|
323 | + if (self::$showBar) { |
|
324 | + self::getBar()->render(); |
|
325 | + } |
|
326 | + |
|
327 | + } else { |
|
328 | + self::fireLog($exception); |
|
329 | + $s = get_class($exception) . ($exception->getMessage() === '' ? '' : ': ' . $exception->getMessage()) |
|
330 | + . ' in ' . $exception->getFile() . ':' . $exception->getLine() |
|
331 | + . "\nStack trace:\n" . $exception->getTraceAsString(); |
|
332 | + try { |
|
333 | + $file = self::log($exception, self::EXCEPTION); |
|
334 | + if ($file && !headers_sent()) { |
|
335 | + header("X-Tracy-Error-Log: $file"); |
|
336 | + } |
|
337 | + echo "$s\n" . ($file ? "(stored in $file)\n" : ''); |
|
338 | + if ($file && self::$browser) { |
|
339 | + exec(self::$browser . ' ' . escapeshellarg($file)); |
|
340 | + } |
|
341 | + } catch (\Exception $e) { |
|
342 | + echo "$s\nUnable to log error: {$e->getMessage()}\n"; |
|
343 | + } catch (\Throwable $e) { |
|
344 | + echo "$s\nUnable to log error: {$e->getMessage()}\n"; |
|
345 | + } |
|
346 | + } |
|
347 | + |
|
348 | + try { |
|
349 | + $e = null; |
|
350 | + foreach (self::$onFatalError as $handler) { |
|
351 | + call_user_func($handler, $exception); |
|
352 | + } |
|
353 | + } catch (\Exception $e) { |
|
354 | + } catch (\Throwable $e) { |
|
355 | + } |
|
356 | + if ($e) { |
|
357 | + try { |
|
358 | + self::log($e, self::EXCEPTION); |
|
359 | + } catch (\Exception $e) { |
|
360 | + } catch (\Throwable $e) { |
|
361 | + } |
|
362 | + } |
|
363 | + |
|
364 | + if ($exit) { |
|
365 | + exit(255); |
|
366 | + } |
|
367 | + } |
|
368 | + |
|
369 | + |
|
370 | + /** |
|
371 | + * Handler to catch warnings and notices. |
|
372 | + * @return bool|null false to call normal error handler, null otherwise |
|
373 | + * @throws ErrorException |
|
374 | + * @internal |
|
375 | + */ |
|
376 | + public static function errorHandler($severity, $message, $file, $line, $context = []) |
|
377 | + { |
|
378 | + if (self::$scream) { |
|
379 | + error_reporting(E_ALL); |
|
380 | + } |
|
381 | + |
|
382 | + if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { |
|
383 | + if (Helpers::findTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), '*::__toString')) { |
|
384 | + $previous = isset($context['e']) && ($context['e'] instanceof \Exception || $context['e'] instanceof \Throwable) ? $context['e'] : null; |
|
385 | + $e = new ErrorException($message, 0, $severity, $file, $line, $previous); |
|
386 | + $e->context = $context; |
|
387 | + self::exceptionHandler($e); |
|
388 | + } |
|
389 | + |
|
390 | + $e = new ErrorException($message, 0, $severity, $file, $line); |
|
391 | + $e->context = $context; |
|
392 | + throw $e; |
|
393 | + |
|
394 | + } elseif (($severity & error_reporting()) !== $severity) { |
|
395 | + return false; // calls normal error handler to fill-in error_get_last() |
|
396 | + |
|
397 | + } elseif (self::$productionMode && ($severity & self::$logSeverity) === $severity) { |
|
398 | + $e = new ErrorException($message, 0, $severity, $file, $line); |
|
399 | + $e->context = $context; |
|
400 | + Helpers::improveException($e); |
|
401 | + try { |
|
402 | + self::log($e, self::ERROR); |
|
403 | + } catch (\Exception $foo) { |
|
404 | + } catch (\Throwable $foo) { |
|
405 | + } |
|
406 | + return null; |
|
407 | + |
|
408 | + } elseif ( |
|
409 | + !self::$productionMode |
|
410 | + && !isset($_GET['_tracy_skip_error']) |
|
411 | + && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity)) |
|
412 | + ) { |
|
413 | + $e = new ErrorException($message, 0, $severity, $file, $line); |
|
414 | + $e->context = $context; |
|
415 | + $e->skippable = true; |
|
416 | + self::exceptionHandler($e); |
|
417 | + } |
|
418 | + |
|
419 | + $message = 'PHP ' . Helpers::errorTypeToString($severity) . ': ' . Helpers::improveError($message, $context); |
|
420 | + $count = &self::getBar()->getPanel('Tracy:errors')->data["$file|$line|$message"]; |
|
421 | + |
|
422 | + if ($count++) { // repeated error |
|
423 | + return null; |
|
424 | + |
|
425 | + } elseif (self::$productionMode) { |
|
426 | + try { |
|
427 | + self::log("$message in $file:$line", self::ERROR); |
|
428 | + } catch (\Exception $foo) { |
|
429 | + } catch (\Throwable $foo) { |
|
430 | + } |
|
431 | + return null; |
|
432 | + |
|
433 | + } else { |
|
434 | + self::fireLog(new ErrorException($message, 0, $severity, $file, $line)); |
|
435 | + return Helpers::isHtmlMode() || Helpers::isAjax() ? null : false; // false calls normal error handler |
|
436 | + } |
|
437 | + } |
|
438 | + |
|
439 | + |
|
440 | + private static function removeOutputBuffers($errorOccurred) |
|
441 | + { |
|
442 | + while (ob_get_level() > self::$obLevel) { |
|
443 | + $status = ob_get_status(); |
|
444 | + if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'], true)) { |
|
445 | + break; |
|
446 | + } |
|
447 | + $fnc = $status['chunk_size'] || !$errorOccurred ? 'ob_end_flush' : 'ob_end_clean'; |
|
448 | + if (!@$fnc()) { // @ may be not removable |
|
449 | + break; |
|
450 | + } |
|
451 | + } |
|
452 | + } |
|
453 | + |
|
454 | + |
|
455 | + /********************* services ****************d*g**/ |
|
456 | + |
|
457 | + |
|
458 | + /** |
|
459 | + * @return BlueScreen |
|
460 | + */ |
|
461 | + public static function getBlueScreen() |
|
462 | + { |
|
463 | + if (!self::$blueScreen) { |
|
464 | + self::$blueScreen = new BlueScreen; |
|
465 | + self::$blueScreen->info = [ |
|
466 | + 'PHP ' . PHP_VERSION, |
|
467 | + isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, |
|
468 | + 'Tracy ' . self::VERSION, |
|
469 | + ]; |
|
470 | + } |
|
471 | + return self::$blueScreen; |
|
472 | + } |
|
473 | + |
|
474 | + |
|
475 | + /** |
|
476 | + * @return Bar |
|
477 | + */ |
|
478 | + public static function getBar() |
|
479 | + { |
|
480 | + if (!self::$bar) { |
|
481 | + self::$bar = new Bar; |
|
482 | + self::$bar->addPanel($info = new DefaultBarPanel('info'), 'Tracy:info'); |
|
483 | + $info->cpuUsage = self::$cpuUsage; |
|
484 | + self::$bar->addPanel(new DefaultBarPanel('errors'), 'Tracy:errors'); // filled by errorHandler() |
|
485 | + } |
|
486 | + return self::$bar; |
|
487 | + } |
|
488 | + |
|
489 | + |
|
490 | + /** |
|
491 | + * @return void |
|
492 | + */ |
|
493 | + public static function setLogger(ILogger $logger) |
|
494 | + { |
|
495 | + self::$logger = $logger; |
|
496 | + } |
|
497 | + |
|
498 | + |
|
499 | + /** |
|
500 | + * @return ILogger |
|
501 | + */ |
|
502 | + public static function getLogger() |
|
503 | + { |
|
504 | + if (!self::$logger) { |
|
505 | + self::$logger = new Logger(self::$logDirectory, self::$email, self::getBlueScreen()); |
|
506 | + self::$logger->directory = &self::$logDirectory; // back compatiblity |
|
507 | + self::$logger->email = &self::$email; |
|
508 | + } |
|
509 | + return self::$logger; |
|
510 | + } |
|
511 | + |
|
512 | + |
|
513 | + /** |
|
514 | + * @return ILogger |
|
515 | + */ |
|
516 | + public static function getFireLogger() |
|
517 | + { |
|
518 | + if (!self::$fireLogger) { |
|
519 | + self::$fireLogger = new FireLogger; |
|
520 | + } |
|
521 | + return self::$fireLogger; |
|
522 | + } |
|
523 | + |
|
524 | + |
|
525 | + /********************* useful tools ****************d*g**/ |
|
526 | + |
|
527 | + |
|
528 | + /** |
|
529 | + * Dumps information about a variable in readable format. |
|
530 | + * @tracySkipLocation |
|
531 | + * @param mixed $var variable to dump |
|
532 | + * @param bool $return return output instead of printing it? (bypasses $productionMode) |
|
533 | + * @return mixed variable itself or dump |
|
534 | + */ |
|
535 | + public static function dump($var, $return = false) |
|
536 | + { |
|
537 | + if ($return) { |
|
538 | + ob_start(function () {}); |
|
539 | + Dumper::dump($var, [ |
|
540 | + Dumper::DEPTH => self::$maxDepth, |
|
541 | + Dumper::TRUNCATE => self::$maxLength, |
|
542 | + ]); |
|
543 | + return ob_get_clean(); |
|
544 | + |
|
545 | + } elseif (!self::$productionMode) { |
|
546 | + Dumper::dump($var, [ |
|
547 | + Dumper::DEPTH => self::$maxDepth, |
|
548 | + Dumper::TRUNCATE => self::$maxLength, |
|
549 | + Dumper::LOCATION => self::$showLocation, |
|
550 | + ]); |
|
551 | + } |
|
552 | + |
|
553 | + return $var; |
|
554 | + } |
|
555 | + |
|
556 | + |
|
557 | + /** |
|
558 | + * Starts/stops stopwatch. |
|
559 | + * @param string $name |
|
560 | + * @return float elapsed seconds |
|
561 | + */ |
|
562 | + public static function timer($name = null) |
|
563 | + { |
|
564 | + static $time = []; |
|
565 | + $now = microtime(true); |
|
566 | + $delta = isset($time[$name]) ? $now - $time[$name] : 0; |
|
567 | + $time[$name] = $now; |
|
568 | + return $delta; |
|
569 | + } |
|
570 | + |
|
571 | + |
|
572 | + /** |
|
573 | + * Dumps information about a variable in Tracy Debug Bar. |
|
574 | + * @tracySkipLocation |
|
575 | + * @param mixed $var |
|
576 | + * @param string $title |
|
577 | + * @param array $options |
|
578 | + * @return mixed variable itself |
|
579 | + */ |
|
580 | + public static function barDump($var, $title = null, array $options = null) |
|
581 | + { |
|
582 | + if (!self::$productionMode) { |
|
583 | + static $panel; |
|
584 | + if (!$panel) { |
|
585 | + self::getBar()->addPanel($panel = new DefaultBarPanel('dumps'), 'Tracy:dumps'); |
|
586 | + } |
|
587 | + $panel->data[] = ['title' => $title, 'dump' => Dumper::toHtml($var, (array) $options + [ |
|
588 | + Dumper::DEPTH => self::$maxDepth, |
|
589 | + Dumper::TRUNCATE => self::$maxLength, |
|
590 | + Dumper::LOCATION => self::$showLocation ?: Dumper::LOCATION_CLASS | Dumper::LOCATION_SOURCE, |
|
591 | + ])]; |
|
592 | + } |
|
593 | + return $var; |
|
594 | + } |
|
595 | + |
|
596 | + |
|
597 | + /** |
|
598 | + * Logs message or exception. |
|
599 | + * @param mixed $message |
|
600 | + * @return mixed |
|
601 | + */ |
|
602 | + public static function log($message, $priority = ILogger::INFO) |
|
603 | + { |
|
604 | + return self::getLogger()->log($message, $priority); |
|
605 | + } |
|
606 | + |
|
607 | + |
|
608 | + /** |
|
609 | + * Sends message to FireLogger console. |
|
610 | + * @param mixed $message |
|
611 | + * @return bool was successful? |
|
612 | + */ |
|
613 | + public static function fireLog($message) |
|
614 | + { |
|
615 | + if (!self::$productionMode && self::$showFireLogger) { |
|
616 | + return self::getFireLogger()->log($message); |
|
617 | + } |
|
618 | + } |
|
619 | + |
|
620 | + |
|
621 | + /** |
|
622 | + * Detects debug mode by IP address. |
|
623 | + * @param string|array $list IP addresses or computer names whitelist detection |
|
624 | + * @return bool |
|
625 | + */ |
|
626 | + public static function detectDebugMode($list = null) |
|
627 | + { |
|
628 | + $addr = isset($_SERVER['REMOTE_ADDR']) |
|
629 | + ? $_SERVER['REMOTE_ADDR'] |
|
630 | + : php_uname('n'); |
|
631 | + $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET]) |
|
632 | + ? $_COOKIE[self::COOKIE_SECRET] |
|
633 | + : null; |
|
634 | + $list = is_string($list) |
|
635 | + ? preg_split('#[,\s]+#', $list) |
|
636 | + : (array) $list; |
|
637 | + if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) { |
|
638 | + $list[] = '127.0.0.1'; |
|
639 | + $list[] = '::1'; |
|
640 | + } |
|
641 | + return in_array($addr, $list, true) || in_array("$secret@$addr", $list, true); |
|
642 | + } |
|
643 | 643 | } |
@@ -169,7 +169,7 @@ discard block |
||
169 | 169 | self::exceptionHandler(new \RuntimeException('Logging directory must be absolute path.')); |
170 | 170 | self::$logDirectory = null; |
171 | 171 | } elseif (!is_dir(self::$logDirectory)) { |
172 | - self::exceptionHandler(new \RuntimeException("Logging directory '" . self::$logDirectory . "' is not found.")); |
|
172 | + self::exceptionHandler(new \RuntimeException("Logging directory '".self::$logDirectory."' is not found.")); |
|
173 | 173 | self::$logDirectory = null; |
174 | 174 | } |
175 | 175 | } |
@@ -214,7 +214,7 @@ discard block |
||
214 | 214 | |
215 | 215 | } elseif (headers_sent($file, $line) || ob_get_length()) { |
216 | 216 | throw new \LogicException( |
217 | - __METHOD__ . '() called after some output has been sent. ' |
|
217 | + __METHOD__.'() called after some output has been sent. ' |
|
218 | 218 | . ($file ? "Output started at $file:$line." : 'Try Tracy\OutputDebugger to find where output started.') |
219 | 219 | ); |
220 | 220 | |
@@ -312,7 +312,7 @@ discard block |
||
312 | 312 | |
313 | 313 | if (Helpers::isHtmlMode()) { |
314 | 314 | $logged = empty($e); |
315 | - require self::$errorTemplate ?: __DIR__ . '/assets/Debugger/error.500.phtml'; |
|
315 | + require self::$errorTemplate ?: __DIR__.'/assets/Debugger/error.500.phtml'; |
|
316 | 316 | } elseif (PHP_SAPI === 'cli') { |
317 | 317 | fwrite(STDERR, 'ERROR: application encountered an error and can not continue. ' |
318 | 318 | . (isset($e) ? "Unable to log error.\n" : "Error was logged.\n")); |
@@ -326,17 +326,17 @@ discard block |
||
326 | 326 | |
327 | 327 | } else { |
328 | 328 | self::fireLog($exception); |
329 | - $s = get_class($exception) . ($exception->getMessage() === '' ? '' : ': ' . $exception->getMessage()) |
|
330 | - . ' in ' . $exception->getFile() . ':' . $exception->getLine() |
|
331 | - . "\nStack trace:\n" . $exception->getTraceAsString(); |
|
329 | + $s = get_class($exception).($exception->getMessage() === '' ? '' : ': '.$exception->getMessage()) |
|
330 | + . ' in '.$exception->getFile().':'.$exception->getLine() |
|
331 | + . "\nStack trace:\n".$exception->getTraceAsString(); |
|
332 | 332 | try { |
333 | 333 | $file = self::log($exception, self::EXCEPTION); |
334 | 334 | if ($file && !headers_sent()) { |
335 | 335 | header("X-Tracy-Error-Log: $file"); |
336 | 336 | } |
337 | - echo "$s\n" . ($file ? "(stored in $file)\n" : ''); |
|
337 | + echo "$s\n".($file ? "(stored in $file)\n" : ''); |
|
338 | 338 | if ($file && self::$browser) { |
339 | - exec(self::$browser . ' ' . escapeshellarg($file)); |
|
339 | + exec(self::$browser.' '.escapeshellarg($file)); |
|
340 | 340 | } |
341 | 341 | } catch (\Exception $e) { |
342 | 342 | echo "$s\nUnable to log error: {$e->getMessage()}\n"; |
@@ -416,7 +416,7 @@ discard block |
||
416 | 416 | self::exceptionHandler($e); |
417 | 417 | } |
418 | 418 | |
419 | - $message = 'PHP ' . Helpers::errorTypeToString($severity) . ': ' . Helpers::improveError($message, $context); |
|
419 | + $message = 'PHP '.Helpers::errorTypeToString($severity).': '.Helpers::improveError($message, $context); |
|
420 | 420 | $count = &self::getBar()->getPanel('Tracy:errors')->data["$file|$line|$message"]; |
421 | 421 | |
422 | 422 | if ($count++) { // repeated error |
@@ -463,9 +463,9 @@ discard block |
||
463 | 463 | if (!self::$blueScreen) { |
464 | 464 | self::$blueScreen = new BlueScreen; |
465 | 465 | self::$blueScreen->info = [ |
466 | - 'PHP ' . PHP_VERSION, |
|
466 | + 'PHP '.PHP_VERSION, |
|
467 | 467 | isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, |
468 | - 'Tracy ' . self::VERSION, |
|
468 | + 'Tracy '.self::VERSION, |
|
469 | 469 | ]; |
470 | 470 | } |
471 | 471 | return self::$blueScreen; |
@@ -535,7 +535,7 @@ discard block |
||
535 | 535 | public static function dump($var, $return = false) |
536 | 536 | { |
537 | 537 | if ($return) { |
538 | - ob_start(function () {}); |
|
538 | + ob_start(function(){}); |
|
539 | 539 | Dumper::dump($var, [ |
540 | 540 | Dumper::DEPTH => self::$maxDepth, |
541 | 541 | Dumper::TRUNCATE => self::$maxLength, |
@@ -419,7 +419,8 @@ discard block |
||
419 | 419 | $message = 'PHP ' . Helpers::errorTypeToString($severity) . ': ' . Helpers::improveError($message, $context); |
420 | 420 | $count = &self::getBar()->getPanel('Tracy:errors')->data["$file|$line|$message"]; |
421 | 421 | |
422 | - if ($count++) { // repeated error |
|
422 | + if ($count++) { |
|
423 | +// repeated error |
|
423 | 424 | return null; |
424 | 425 | |
425 | 426 | } elseif (self::$productionMode) { |
@@ -445,7 +446,8 @@ discard block |
||
445 | 446 | break; |
446 | 447 | } |
447 | 448 | $fnc = $status['chunk_size'] || !$errorOccurred ? 'ob_end_flush' : 'ob_end_clean'; |
448 | - if (!@$fnc()) { // @ may be not removable |
|
449 | + if (!@$fnc()) { |
|
450 | +// @ may be not removable |
|
449 | 451 | break; |
450 | 452 | } |
451 | 453 | } |