Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Bar
  • BlueScreen
  • Debugger
  • Dumper
  • FireLogger
  • Helpers
  • Logger
  • OutputDebugger

Interfaces

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