PageRenderTime 50ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/external/tracy/src/Tracy/BlueScreen.php

https://bitbucket.org/navigatecms/navigatecms
PHP | 269 lines | 203 code | 28 blank | 38 comment | 17 complexity | 3b2c9d1770f82615fdfca798a91d1cc0 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-2.1, BSD-3-Clause, AGPL-3.0, Apache-2.0
  1. <?php
  2. /**
  3. * This file is part of the Tracy (https://tracy.nette.org)
  4. * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  5. */
  6. namespace Tracy;
  7. /**
  8. * Red BlueScreen.
  9. */
  10. class BlueScreen
  11. {
  12. /** @var string[] */
  13. public $info = array();
  14. /** @var callable[] */
  15. private $panels = array();
  16. /** @var string[] paths to be collapsed in stack trace (e.g. core libraries) */
  17. public $collapsePaths = array();
  18. /** @var int */
  19. public $maxDepth = 3;
  20. /** @var int */
  21. public $maxLength = 150;
  22. public function __construct()
  23. {
  24. $this->collapsePaths[] = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy$#', strtr(__DIR__, '\\', '/'), $m)
  25. ? $m[1]
  26. : __DIR__;
  27. }
  28. /**
  29. * Add custom panel.
  30. * @param callable
  31. * @return static
  32. */
  33. public function addPanel($panel)
  34. {
  35. if (!in_array($panel, $this->panels, TRUE)) {
  36. $this->panels[] = $panel;
  37. }
  38. return $this;
  39. }
  40. /**
  41. * Renders blue screen.
  42. * @param \Exception|\Throwable
  43. * @return void
  44. */
  45. public function render($exception)
  46. {
  47. if (Helpers::isAjax() && session_status() === PHP_SESSION_ACTIVE) {
  48. ob_start(function () {});
  49. $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/content.phtml');
  50. $contentId = $_SERVER['HTTP_X_TRACY_AJAX'];
  51. $_SESSION['_tracy']['bluescreen'][$contentId] = array('content' => ob_get_clean(), 'dumps' => Dumper::fetchLiveData(), 'time' => time());
  52. } else {
  53. $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml');
  54. }
  55. }
  56. /**
  57. * Renders blue screen to file (if file exists, it will not be overwritten).
  58. * @param \Exception|\Throwable
  59. * @param string file path
  60. * @return void
  61. */
  62. public function renderToFile($exception, $file)
  63. {
  64. if ($handle = @fopen($file, 'x')) {
  65. ob_start(); // double buffer prevents sending HTTP headers in some PHP
  66. ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096);
  67. $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml');
  68. ob_end_flush();
  69. ob_end_clean();
  70. fclose($handle);
  71. }
  72. }
  73. private function renderTemplate($exception, $template)
  74. {
  75. $info = array_filter($this->info);
  76. $source = Helpers::getSource();
  77. $sourceIsUrl = preg_match('#^https?://#', $source);
  78. $title = $exception instanceof \ErrorException
  79. ? Helpers::errorTypeToString($exception->getSeverity())
  80. : Helpers::getClass($exception);
  81. $skipError = $sourceIsUrl && $exception instanceof \ErrorException && !empty($exception->skippable)
  82. ? $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error'
  83. : NULL;
  84. $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? NULL : error_get_last();
  85. $dump = function($v) {
  86. return Dumper::toHtml($v, array(
  87. Dumper::DEPTH => $this->maxDepth,
  88. Dumper::TRUNCATE => $this->maxLength,
  89. Dumper::LIVE => TRUE,
  90. Dumper::LOCATION => Dumper::LOCATION_CLASS,
  91. ));
  92. };
  93. $nonce = Helpers::getNonce();
  94. require $template;
  95. }
  96. /**
  97. * @return \stdClass[]
  98. */
  99. private function renderPanels($ex)
  100. {
  101. $obLevel = ob_get_level();
  102. $res = array();
  103. foreach ($this->panels as $callback) {
  104. try {
  105. $panel = call_user_func($callback, $ex);
  106. if (empty($panel['tab']) || empty($panel['panel'])) {
  107. continue;
  108. }
  109. $res[] = (object) $panel;
  110. continue;
  111. } catch (\Throwable $e) {
  112. } catch (\Exception $e) {
  113. }
  114. while (ob_get_level() > $obLevel) { // restore ob-level if broken
  115. ob_end_clean();
  116. }
  117. is_callable($callback, TRUE, $name);
  118. $res[] = (object) array(
  119. 'tab' => "Error in panel $name",
  120. 'panel' => nl2br(Helpers::escapeHtml($e)),
  121. );
  122. }
  123. return $res;
  124. }
  125. /**
  126. * Returns syntax highlighted source code.
  127. * @param string
  128. * @param int
  129. * @param int
  130. * @return string|NULL
  131. */
  132. public static function highlightFile($file, $line, $lines = 15, array $vars = NULL)
  133. {
  134. $source = @file_get_contents($file); // @ file may not exist
  135. if ($source) {
  136. $source = static::highlightPhp($source, $line, $lines, $vars);
  137. if ($editor = Helpers::editorUri($file, $line)) {
  138. $source = substr_replace($source, ' data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0);
  139. }
  140. return $source;
  141. }
  142. }
  143. /**
  144. * Returns syntax highlighted source code.
  145. * @param string
  146. * @param int
  147. * @param int
  148. * @return string
  149. */
  150. public static function highlightPhp($source, $line, $lines = 15, array $vars = NULL)
  151. {
  152. if (function_exists('ini_set')) {
  153. ini_set('highlight.comment', '#998; font-style: italic');
  154. ini_set('highlight.default', '#000');
  155. ini_set('highlight.html', '#06B');
  156. ini_set('highlight.keyword', '#D24; font-weight: bold');
  157. ini_set('highlight.string', '#080');
  158. }
  159. $source = str_replace(array("\r\n", "\r"), "\n", $source);
  160. $source = explode("\n", highlight_string($source, TRUE));
  161. $out = $source[0]; // <code><span color=highlight.html>
  162. $source = str_replace('<br />', "\n", $source[1]);
  163. $out .= static::highlightLine($source, $line, $lines);
  164. if ($vars) {
  165. $out = preg_replace_callback('#">\$(\w+)(&nbsp;)?</span>#', function ($m) use ($vars) {
  166. return array_key_exists($m[1], $vars)
  167. ? '" title="'
  168. . str_replace('"', '&quot;', trim(strip_tags(Dumper::toHtml($vars[$m[1]], array(Dumper::DEPTH => 1)))))
  169. . $m[0]
  170. : $m[0];
  171. }, $out);
  172. }
  173. $out = str_replace('&nbsp;', ' ', $out);
  174. return "<pre class='code'><div>$out</div></pre>";
  175. }
  176. /**
  177. * Returns highlighted line in HTML code.
  178. * @return string
  179. */
  180. public static function highlightLine($html, $line, $lines = 15)
  181. {
  182. $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html));
  183. $out = '';
  184. $spans = 1;
  185. $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3));
  186. while (--$i >= 1) { // find last highlighted block
  187. if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) {
  188. if ($m[1] !== '</span>') {
  189. $spans++;
  190. $out .= $m[1];
  191. }
  192. break;
  193. }
  194. }
  195. $source = array_slice($source, $start, $lines, TRUE);
  196. end($source);
  197. $numWidth = strlen((string) key($source));
  198. foreach ($source as $n => $s) {
  199. $spans += substr_count($s, '<span') - substr_count($s, '</span');
  200. $s = str_replace(array("\r", "\n"), array('', ''), $s);
  201. preg_match_all('#<[^>]+>#', $s, $tags);
  202. if ($n == $line) {
  203. $out .= sprintf(
  204. "<span class='highlight'>%{$numWidth}s: %s\n</span>%s",
  205. $n,
  206. strip_tags($s),
  207. implode('', $tags[0])
  208. );
  209. } else {
  210. $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s);
  211. }
  212. }
  213. $out .= str_repeat('</span>', $spans) . '</code>';
  214. return $out;
  215. }
  216. /**
  217. * Should a file be collapsed in stack trace?
  218. * @param string
  219. * @return bool
  220. */
  221. public function isCollapsed($file)
  222. {
  223. $file = strtr($file, '\\', '/') . '/';
  224. foreach ($this->collapsePaths as $path) {
  225. $path = strtr($path, '\\', '/') . '/';
  226. if (strncmp($file, $path, strlen($path)) === 0) {
  227. return TRUE;
  228. }
  229. }
  230. return FALSE;
  231. }
  232. }