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
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Bar
  • BlueScreen
  • Debugger
  • DefaultBarPanel
  • 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:  * Dumps a variable.
 13:  */
 14: class Dumper
 15: {
 16:     const
 17:         DEPTH = 'depth', // how many nested levels of array/object properties display (defaults to 4)
 18:         TRUNCATE = 'truncate', // how truncate long strings? (defaults to 150)
 19:         COLLAPSE = 'collapse', // collapse top array/object or how big are collapsed? (defaults to 14)
 20:         COLLAPSE_COUNT = 'collapsecount', // how big array/object are collapsed? (defaults to 7)
 21:         LOCATION = 'location', // show location string? (defaults to 0)
 22:         OBJECT_EXPORTERS = 'exporters', // custom exporters for objects (defaults to Dumper::$objectexporters)
 23:         LIVE = 'live', // will be rendered using JavaScript
 24:         DEBUGINFO = 'debuginfo', // use magic method __debugInfo if exists (defaults to false)
 25:         KEYS_TO_HIDE = 'keystohide'; // sensitive keys not displayed (defaults to [])
 26: 
 27:     const
 28:         LOCATION_SOURCE = 0b0001, // shows where dump was called
 29:         LOCATION_LINK = 0b0010, // appends clickable anchor
 30:         LOCATION_CLASS = 0b0100; // shows where class is defined
 31: 
 32:     const
 33:         HIDDEN_VALUE = '*****';
 34: 
 35:     /** @var array */
 36:     public static $terminalColors = [
 37:         'bool' => '1;33',
 38:         'null' => '1;33',
 39:         'number' => '1;32',
 40:         'string' => '1;36',
 41:         'array' => '1;31',
 42:         'key' => '1;37',
 43:         'object' => '1;31',
 44:         'visibility' => '1;30',
 45:         'resource' => '1;37',
 46:         'indent' => '1;30',
 47:     ];
 48: 
 49:     /** @var array */
 50:     public static $resources = [
 51:         'stream' => 'stream_get_meta_data',
 52:         'stream-context' => 'stream_context_get_options',
 53:         'curl' => 'curl_getinfo',
 54:     ];
 55: 
 56:     /** @var array */
 57:     public static $objectExporters = [
 58:         'Closure' => 'Tracy\Dumper::exportClosure',
 59:         'SplFileInfo' => 'Tracy\Dumper::exportSplFileInfo',
 60:         'SplObjectStorage' => 'Tracy\Dumper::exportSplObjectStorage',
 61:         '__PHP_Incomplete_Class' => 'Tracy\Dumper::exportPhpIncompleteClass',
 62:     ];
 63: 
 64:     /** @var string @internal */
 65:     public static $livePrefix;
 66: 
 67:     /** @var array  */
 68:     private static $liveStorage = [];
 69: 
 70: 
 71:     /**
 72:      * Dumps variable to the output.
 73:      * @return mixed  variable
 74:      */
 75:     public static function dump($var, array $options = null)
 76:     {
 77:         if (PHP_SAPI !== 'cli' && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()))) {
 78:             echo self::toHtml($var, $options);
 79:         } elseif (self::detectColors()) {
 80:             echo self::toTerminal($var, $options);
 81:         } else {
 82:             echo self::toText($var, $options);
 83:         }
 84:         return $var;
 85:     }
 86: 
 87: 
 88:     /**
 89:      * Dumps variable to HTML.
 90:      * @return string
 91:      */
 92:     public static function toHtml($var, array $options = null)
 93:     {
 94:         $options = (array) $options + [
 95:             self::DEPTH => 4,
 96:             self::TRUNCATE => 150,
 97:             self::COLLAPSE => 14,
 98:             self::COLLAPSE_COUNT => 7,
 99:             self::OBJECT_EXPORTERS => null,
100:             self::DEBUGINFO => false,
101:             self::KEYS_TO_HIDE => [],
102:         ];
103:         $loc = &$options[self::LOCATION];
104:         $loc = $loc === true ? ~0 : (int) $loc;
105: 
106:         $options[self::KEYS_TO_HIDE] = array_flip(array_map('strtolower', $options[self::KEYS_TO_HIDE]));
107:         $options[self::OBJECT_EXPORTERS] = (array) $options[self::OBJECT_EXPORTERS] + self::$objectExporters;
108:         uksort($options[self::OBJECT_EXPORTERS], function ($a, $b) {
109:             return $b === '' || (class_exists($a, false) && is_subclass_of($a, $b)) ? -1 : 1;
110:         });
111: 
112:         $live = !empty($options[self::LIVE]) && $var && (is_array($var) || is_object($var) || is_resource($var));
113:         list($file, $line, $code) = $loc ? self::findLocation() : null;
114:         $locAttrs = $file && $loc & self::LOCATION_SOURCE ? Helpers::formatHtml(
115:             ' title="%in file % on line %" data-tracy-href="%"', "$code\n", $file, $line, Helpers::editorUri($file, $line)
116:         ) : null;
117: 
118:         return '<pre class="tracy-dump' . ($live && $options[self::COLLAPSE] === true ? ' tracy-collapsed' : '') . '"'
119:             . $locAttrs
120:             . ($live ? " data-tracy-dump='" . json_encode(self::toJson($var, $options), JSON_HEX_APOS | JSON_HEX_AMP) . "'>" : '>')
121:             . ($live ? '' : self::dumpVar($var, $options))
122:             . ($file && $loc & self::LOCATION_LINK ? '<small>in ' . Helpers::editorLink($file, $line) . '</small>' : '')
123:             . "</pre>\n";
124:     }
125: 
126: 
127:     /**
128:      * Dumps variable to plain text.
129:      * @return string
130:      */
131:     public static function toText($var, array $options = null)
132:     {
133:         return htmlspecialchars_decode(strip_tags(self::toHtml($var, $options)), ENT_QUOTES);
134:     }
135: 
136: 
137:     /**
138:      * Dumps variable to x-terminal.
139:      * @return string
140:      */
141:     public static function toTerminal($var, array $options = null)
142:     {
143:         return htmlspecialchars_decode(strip_tags(preg_replace_callback('#<span class="tracy-dump-(\w+)">|</span>#', function ($m) {
144:             return "\033[" . (isset($m[1], self::$terminalColors[$m[1]]) ? self::$terminalColors[$m[1]] : '0') . 'm';
145:         }, self::toHtml($var, $options))), ENT_QUOTES);
146:     }
147: 
148: 
149:     /**
150:      * Internal toHtml() dump implementation.
151:      * @param  mixed  $var
152:      * @param  array  $options
153:      * @param  int  $level  recursion level
154:      * @return string
155:      */
156:     private static function dumpVar(&$var, array $options, $level = 0)
157:     {
158:         if (method_exists(__CLASS__, $m = 'dump' . gettype($var))) {
159:             return self::$m($var, $options, $level);
160:         } else {
161:             return "<span>unknown type</span>\n";
162:         }
163:     }
164: 
165: 
166:     private static function dumpNull()
167:     {
168:         return "<span class=\"tracy-dump-null\">null</span>\n";
169:     }
170: 
171: 
172:     private static function dumpBoolean(&$var)
173:     {
174:         return '<span class="tracy-dump-bool">' . ($var ? 'true' : 'false') . "</span>\n";
175:     }
176: 
177: 
178:     private static function dumpInteger(&$var)
179:     {
180:         return "<span class=\"tracy-dump-number\">$var</span>\n";
181:     }
182: 
183: 
184:     private static function dumpDouble(&$var)
185:     {
186:         $var = is_finite($var)
187:             ? ($tmp = json_encode($var)) . (strpos($tmp, '.') === false ? '.0' : '')
188:             : str_replace('.0', '', var_export($var, true)); // workaround for PHP 7.0.2
189:         return "<span class=\"tracy-dump-number\">$var</span>\n";
190:     }
191: 
192: 
193:     private static function dumpString(&$var, $options)
194:     {
195:         return '<span class="tracy-dump-string">"'
196:             . Helpers::escapeHtml(self::encodeString($var, $options[self::TRUNCATE]))
197:             . '"</span>' . (strlen($var) > 1 ? ' (' . strlen($var) . ')' : '') . "\n";
198:     }
199: 
200: 
201:     private static function dumpArray(&$var, $options, $level)
202:     {
203:         static $marker;
204:         if ($marker === null) {
205:             $marker = uniqid("\x00", true);
206:         }
207: 
208:         $out = '<span class="tracy-dump-array">array</span> (';
209: 
210:         if (empty($var)) {
211:             return $out . ")\n";
212: 
213:         } elseif (isset($var[$marker])) {
214:             return $out . (count($var) - 1) . ") [ <i>RECURSION</i> ]\n";
215: 
216:         } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH]) {
217:             $collapsed = $level ? count($var) >= $options[self::COLLAPSE_COUNT]
218:                 : (is_int($options[self::COLLAPSE]) ? count($var) >= $options[self::COLLAPSE] : $options[self::COLLAPSE]);
219:             $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">'
220:                 . $out . count($var) . ")</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>';
221:             $var[$marker] = true;
222:             foreach ($var as $k => &$v) {
223:                 if ($k !== $marker) {
224:                     $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]) ? self::HIDDEN_VALUE : null;
225:                     $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . Helpers::escapeHtml(self::encodeString($k, $options[self::TRUNCATE])) . '"';
226:                     $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
227:                         . '<span class="tracy-dump-key">' . $k . '</span> => '
228:                         . ($hide ? self::dumpString($hide, $options) : self::dumpVar($v, $options, $level + 1));
229:                 }
230:             }
231:             unset($var[$marker]);
232:             return $out . '</div>';
233: 
234:         } else {
235:             return $out . count($var) . ") [ ... ]\n";
236:         }
237:     }
238: 
239: 
240:     private static function dumpObject(&$var, $options, $level)
241:     {
242:         $fields = self::exportObject($var, $options[self::OBJECT_EXPORTERS], $options[self::DEBUGINFO]);
243: 
244:         $editorAttributes = '';
245:         if ($options[self::LOCATION] & self::LOCATION_CLASS) {
246:             $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var);
247:             $editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine());
248:             if ($editor) {
249:                 $editorAttributes = Helpers::formatHtml(
250:                     ' title="Declared in file % on line %" data-tracy-href="%"',
251:                     $rc->getFileName(),
252:                     $rc->getStartLine(),
253:                     $editor
254:                 );
255:             }
256:         }
257:         $out = '<span class="tracy-dump-object"' . $editorAttributes . '>'
258:             . Helpers::escapeHtml(Helpers::getClass($var))
259:             . '</span> <span class="tracy-dump-hash">#' . substr(md5(spl_object_hash($var)), 0, 4) . '</span>';
260: 
261:         static $list = [];
262: 
263:         if (empty($fields)) {
264:             return $out . "\n";
265: 
266:         } elseif (in_array($var, $list, true)) {
267:             return $out . " { <i>RECURSION</i> }\n";
268: 
269:         } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH] || $var instanceof \Closure) {
270:             $collapsed = $level ? count($fields) >= $options[self::COLLAPSE_COUNT]
271:                 : (is_int($options[self::COLLAPSE]) ? count($fields) >= $options[self::COLLAPSE] : $options[self::COLLAPSE]);
272:             $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">'
273:                 . $out . "</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>';
274:             $list[] = $var;
275:             foreach ($fields as $k => &$v) {
276:                 $vis = '';
277:                 if (isset($k[0]) && $k[0] === "\x00") {
278:                     $vis = ' <span class="tracy-dump-visibility">' . ($k[1] === '*' ? 'protected' : 'private') . '</span>';
279:                     $k = substr($k, strrpos($k, "\x00") + 1);
280:                 }
281:                 $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]) ? self::HIDDEN_VALUE : null;
282:                 $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . Helpers::escapeHtml(self::encodeString($k, $options[self::TRUNCATE])) . '"';
283:                 $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
284:                     . '<span class="tracy-dump-key">' . $k . "</span>$vis => "
285:                     . ($hide ? self::dumpString($hide, $options) : self::dumpVar($v, $options, $level + 1));
286:             }
287:             array_pop($list);
288:             return $out . '</div>';
289: 
290:         } else {
291:             return $out . " { ... }\n";
292:         }
293:     }
294: 
295: 
296:     private static function dumpResource(&$var, $options, $level)
297:     {
298:         $type = get_resource_type($var);
299:         $out = '<span class="tracy-dump-resource">' . Helpers::escapeHtml($type) . ' resource</span> '
300:             . '<span class="tracy-dump-hash">#' . (int) $var . '</span>';
301:         if (isset(self::$resources[$type])) {
302:             $out = "<span class=\"tracy-toggle tracy-collapsed\">$out</span>\n<div class=\"tracy-collapsed\">";
303:             foreach (call_user_func(self::$resources[$type], $var) as $k => $v) {
304:                 $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
305:                     . '<span class="tracy-dump-key">' . Helpers::escapeHtml($k) . '</span> => ' . self::dumpVar($v, $options, $level + 1);
306:             }
307:             return $out . '</div>';
308:         }
309:         return "$out\n";
310:     }
311: 
312: 
313:     /**
314:      * @return mixed
315:      */
316:     private static function toJson(&$var, $options, $level = 0)
317:     {
318:         if (is_bool($var) || $var === null || is_int($var)) {
319:             return $var;
320: 
321:         } elseif (is_float($var)) {
322:             return is_finite($var)
323:                 ? (strpos($tmp = json_encode($var), '.') ? $var : ['number' => "$tmp.0"])
324:                 : ['type' => (string) $var];
325: 
326:         } elseif (is_string($var)) {
327:             return self::encodeString($var, $options[self::TRUNCATE]);
328: 
329:         } elseif (is_array($var)) {
330:             static $marker;
331:             if ($marker === null) {
332:                 $marker = uniqid("\x00", true);
333:             }
334:             if (isset($var[$marker]) || $level >= $options[self::DEPTH]) {
335:                 return [null];
336:             }
337:             $res = [];
338:             $var[$marker] = true;
339:             foreach ($var as $k => &$v) {
340:                 if ($k !== $marker) {
341:                     $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]);
342:                     $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . self::encodeString($k, $options[self::TRUNCATE]) . '"';
343:                     $res[] = [$k, $hide ? self::HIDDEN_VALUE : self::toJson($v, $options, $level + 1)];
344:                 }
345:             }
346:             unset($var[$marker]);
347:             return $res;
348: 
349:         } elseif (is_object($var)) {
350:             $obj = &self::$liveStorage[spl_object_hash($var)];
351:             if ($obj && $obj['level'] <= $level) {
352:                 return ['object' => $obj['id']];
353:             }
354: 
355:             $editorInfo = null;
356:             if ($options[self::LOCATION] & self::LOCATION_CLASS) {
357:                 $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var);
358:                 $editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine());
359:                 $editorInfo = $editor ? ['file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'url' => $editor] : null;
360:             }
361:             static $counter = 1;
362:             $obj = $obj ?: [
363:                 'id' => self::$livePrefix . '0' . $counter++, // differentiate from resources
364:                 'name' => Helpers::getClass($var),
365:                 'editor' => $editorInfo,
366:                 'level' => $level,
367:                 'object' => $var,
368:             ];
369: 
370:             if ($level < $options[self::DEPTH] || !$options[self::DEPTH]) {
371:                 $obj['level'] = $level;
372:                 $obj['items'] = [];
373: 
374:                 foreach (self::exportObject($var, $options[self::OBJECT_EXPORTERS], $options[self::DEBUGINFO]) as $k => $v) {
375:                     $vis = 0;
376:                     if (isset($k[0]) && $k[0] === "\x00") {
377:                         $vis = $k[1] === '*' ? 1 : 2;
378:                         $k = substr($k, strrpos($k, "\x00") + 1);
379:                     }
380:                     $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]);
381:                     $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . self::encodeString($k, $options[self::TRUNCATE]) . '"';
382:                     $obj['items'][] = [$k, $hide ? self::HIDDEN_VALUE : self::toJson($v, $options, $level + 1), $vis];
383:                 }
384:             }
385:             return ['object' => $obj['id']];
386: 
387:         } elseif (is_resource($var)) {
388:             $obj = &self::$liveStorage[(string) $var];
389:             if (!$obj) {
390:                 $type = get_resource_type($var);
391:                 $obj = ['id' => self::$livePrefix . (int) $var, 'name' => $type . ' resource'];
392:                 if (isset(self::$resources[$type])) {
393:                     foreach (call_user_func(self::$resources[$type], $var) as $k => $v) {
394:                         $obj['items'][] = [$k, self::toJson($v, $options, $level + 1)];
395:                     }
396:                 }
397:             }
398:             return ['resource' => $obj['id']];
399: 
400:         } else {
401:             return ['type' => 'unknown type'];
402:         }
403:     }
404: 
405: 
406:     /** @return array  */
407:     public static function fetchLiveData()
408:     {
409:         $res = [];
410:         foreach (self::$liveStorage as $obj) {
411:             $id = $obj['id'];
412:             unset($obj['level'], $obj['object'], $obj['id']);
413:             $res[$id] = $obj;
414:         }
415:         self::$liveStorage = [];
416:         return $res;
417:     }
418: 
419: 
420:     /**
421:      * @internal
422:      * @return string UTF-8
423:      */
424:     public static function encodeString($s, $maxLength = null)
425:     {
426:         static $table;
427:         if ($table === null) {
428:             foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
429:                 $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
430:             }
431:             $table['\\'] = '\\\\';
432:             $table["\r"] = '\r';
433:             $table["\n"] = '\n';
434:             $table["\t"] = '\t';
435:         }
436: 
437:         if ($maxLength && strlen($s) > $maxLength) { // shortens to $maxLength in UTF-8 or longer
438:             if (function_exists('mb_substr')) {
439:                 $s = mb_substr($tmp = $s, 0, $maxLength, 'UTF-8');
440:                 $shortened = $s !== $tmp;
441:             } else {
442:                 $i = $len = 0;
443:                 $maxI = $maxLength * 4; // max UTF-8 length
444:                 do {
445:                     if (($s[$i] < "\x80" || $s[$i] >= "\xC0") && (++$len > $maxLength) || $i >= $maxI) {
446:                         $s = substr($s, 0, $i);
447:                         $shortened = true;
448:                         break;
449:                     }
450:                 } while (isset($s[++$i]));
451:             }
452:         }
453: 
454:         if (preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $s) || preg_last_error()) { // is binary?
455:             if ($maxLength && strlen($s) > $maxLength) {
456:                 $s = substr($s, 0, $maxLength);
457:                 $shortened = true;
458:             }
459:             $s = strtr($s, $table);
460:         }
461: 
462:         return $s . (empty($shortened) ? '' : ' ... ');
463:     }
464: 
465: 
466:     /**
467:      * @return array
468:      */
469:     private static function exportObject($obj, array $exporters, $useDebugInfo)
470:     {
471:         foreach ($exporters as $type => $dumper) {
472:             if (!$type || $obj instanceof $type) {
473:                 return call_user_func($dumper, $obj);
474:             }
475:         }
476: 
477:         if ($useDebugInfo && method_exists($obj, '__debugInfo')) {
478:             return $obj->__debugInfo();
479:         }
480: 
481:         return (array) $obj;
482:     }
483: 
484: 
485:     /**
486:      * @return array
487:      */
488:     private static function exportClosure(\Closure $obj)
489:     {
490:         $rc = new \ReflectionFunction($obj);
491:         $res = [];
492:         foreach ($rc->getParameters() as $param) {
493:             $res[] = '$' . $param->getName();
494:         }
495:         return [
496:             'file' => $rc->getFileName(),
497:             'line' => $rc->getStartLine(),
498:             'variables' => $rc->getStaticVariables(),
499:             'parameters' => implode(', ', $res),
500:         ];
501:     }
502: 
503: 
504:     /**
505:      * @return array
506:      */
507:     private static function exportSplFileInfo(\SplFileInfo $obj)
508:     {
509:         return ['path' => $obj->getPathname()];
510:     }
511: 
512: 
513:     /**
514:      * @return array
515:      */
516:     private static function exportSplObjectStorage(\SplObjectStorage $obj)
517:     {
518:         $res = [];
519:         foreach (clone $obj as $item) {
520:             $res[] = ['object' => $item, 'data' => $obj[$item]];
521:         }
522:         return $res;
523:     }
524: 
525: 
526:     /**
527:      * @return array
528:      */
529:     private static function exportPhpIncompleteClass(\__PHP_Incomplete_Class $obj)
530:     {
531:         $info = ['className' => null, 'private' => [], 'protected' => [], 'public' => []];
532:         foreach ((array) $obj as $name => $value) {
533:             if ($name === '__PHP_Incomplete_Class_Name') {
534:                 $info['className'] = $value;
535:             } elseif (preg_match('#^\x0\*\x0(.+)\z#', $name, $m)) {
536:                 $info['protected'][$m[1]] = $value;
537:             } elseif (preg_match('#^\x0(.+)\x0(.+)\z#', $name, $m)) {
538:                 $info['private'][$m[1] . '::$' . $m[2]] = $value;
539:             } else {
540:                 $info['public'][$name] = $value;
541:             }
542:         }
543:         return $info;
544:     }
545: 
546: 
547:     /**
548:      * Finds the location where dump was called.
549:      * @return array|null [file, line, code]
550:      */
551:     private static function findLocation()
552:     {
553:         foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $item) {
554:             if (isset($item['class']) && $item['class'] === __CLASS__) {
555:                 $location = $item;
556:                 continue;
557:             } elseif (isset($item['function'])) {
558:                 try {
559:                     $reflection = isset($item['class'])
560:                         ? new \ReflectionMethod($item['class'], $item['function'])
561:                         : new \ReflectionFunction($item['function']);
562:                     if ($reflection->isInternal() || preg_match('#\s@tracySkipLocation\s#', (string) $reflection->getDocComment())) {
563:                         $location = $item;
564:                         continue;
565:                     }
566:                 } catch (\ReflectionException $e) {
567:                 }
568:             }
569:             break;
570:         }
571: 
572:         if (isset($location['file'], $location['line']) && is_file($location['file'])) {
573:             $lines = file($location['file']);
574:             $line = $lines[$location['line'] - 1];
575:             return [
576:                 $location['file'],
577:                 $location['line'],
578:                 trim(preg_match('#\w*dump(er::\w+)?\(.*\)#i', $line, $m) ? $m[0] : $line),
579:             ];
580:         }
581:     }
582: 
583: 
584:     /**
585:      * @return bool
586:      */
587:     private static function detectColors()
588:     {
589:         return self::$terminalColors &&
590:             (getenv('ConEmuANSI') === 'ON'
591:             || getenv('ANSICON') !== false
592:             || getenv('term') === 'xterm-256color'
593:             || (defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT)));
594:     }
595: }
596: 
Nette 2.4-20191120 API API documentation generated by ApiGen 2.8.0