Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

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

Interfaces

  • IBarPanel
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  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: use Tracy;
 11: 
 12: 
 13: /**
 14:  * Dumps a variable.
 15:  *
 16:  * @author     David Grudl
 17:  */
 18: class Dumper
 19: {
 20:     const DEPTH = 'depth', // how many nested levels of array/object properties display (defaults to 4)
 21:         TRUNCATE = 'truncate', // how truncate long strings? (defaults to 150)
 22:         COLLAPSE = 'collapse', // always collapse? (defaults to false)
 23:         COLLAPSE_COUNT = 'collapsecount', // how big array/object are collapsed? (defaults to 7)
 24:         LOCATION = 'location'; // show location string? (defaults to false)
 25: 
 26:     /** @var array */
 27:     public static $terminalColors = array(
 28:         'bool' => '1;33',
 29:         'null' => '1;33',
 30:         'number' => '1;32',
 31:         'string' => '1;36',
 32:         'array' => '1;31',
 33:         'key' => '1;37',
 34:         'object' => '1;31',
 35:         'visibility' => '1;30',
 36:         'resource' => '1;37',
 37:         'indent' => '1;30',
 38:     );
 39: 
 40:     /** @var array */
 41:     public static $resources = array(
 42:         'stream' => 'stream_get_meta_data',
 43:         'stream-context' => 'stream_context_get_options',
 44:         'curl' => 'curl_getinfo',
 45:     );
 46: 
 47: 
 48:     /**
 49:      * Dumps variable to the output.
 50:      * @return mixed  variable
 51:      */
 52:     public static function dump($var, array $options = NULL)
 53:     {
 54:         if (PHP_SAPI !== 'cli' && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()))) {
 55:             echo self::toHtml($var, $options);
 56:         } elseif (self::detectColors()) {
 57:             echo self::toTerminal($var, $options);
 58:         } else {
 59:             echo self::toText($var, $options);
 60:         }
 61:         return $var;
 62:     }
 63: 
 64: 
 65:     /**
 66:      * Dumps variable to HTML.
 67:      * @return string
 68:      */
 69:     public static function toHtml($var, array $options = NULL)
 70:     {
 71:         $options = (array) $options + array(
 72:             self::DEPTH => 4,
 73:             self::TRUNCATE => 150,
 74:             self::COLLAPSE => FALSE,
 75:             self::COLLAPSE_COUNT => 7,
 76:             self::LOCATION => FALSE,
 77:         );
 78:         list($file, $line, $code) = $options[self::LOCATION] ? self::findLocation() : NULL;
 79:         return '<pre class="tracy-dump"'
 80:             . ($file ? Helpers::createHtml(' title="%in file % on line %" data-tracy-href="%"', "$code\n", $file, $line, Helpers::editorUri($file, $line)) . '>' : '>')
 81:             . self::dumpVar($var, $options)
 82:             . ($file ? '<small>in ' . Helpers::editorLink($file, $line) . '</small>' : '')
 83:             . "</pre>\n";
 84:     }
 85: 
 86: 
 87:     /**
 88:      * Dumps variable to plain text.
 89:      * @return string
 90:      */
 91:     public static function toText($var, array $options = NULL)
 92:     {
 93:         return htmlspecialchars_decode(strip_tags(self::toHtml($var, $options)), ENT_QUOTES);
 94:     }
 95: 
 96: 
 97:     /**
 98:      * Dumps variable to x-terminal.
 99:      * @return string
100:      */
101:     public static function toTerminal($var, array $options = NULL)
102:     {
103:         return htmlspecialchars_decode(strip_tags(preg_replace_callback('#<span class="tracy-dump-(\w+)">|</span>#', function ($m) {
104:             return "\033[" . (isset($m[1], Dumper::$terminalColors[$m[1]]) ? Dumper::$terminalColors[$m[1]] : '0') . 'm';
105:         }, self::toHtml($var, $options))), ENT_QUOTES);
106:     }
107: 
108: 
109:     /**
110:      * Internal toHtml() dump implementation.
111:      * @param  mixed  variable to dump
112:      * @param  array  options
113:      * @param  int    current recursion level
114:      * @return string
115:      */
116:     private static function dumpVar(& $var, array $options, $level = 0)
117:     {
118:         if (method_exists(__CLASS__, $m = 'dump' . gettype($var))) {
119:             return self::$m($var, $options, $level);
120:         } else {
121:             return "<span>unknown type</span>\n";
122:         }
123:     }
124: 
125: 
126:     private static function dumpNull()
127:     {
128:         return "<span class=\"tracy-dump-null\">NULL</span>\n";
129:     }
130: 
131: 
132:     private static function dumpBoolean(& $var)
133:     {
134:         return '<span class="tracy-dump-bool">' . ($var ? 'TRUE' : 'FALSE') . "</span>\n";
135:     }
136: 
137: 
138:     private static function dumpInteger(& $var)
139:     {
140:         return "<span class=\"tracy-dump-number\">$var</span>\n";
141:     }
142: 
143: 
144:     private static function dumpDouble(& $var)
145:     {
146:         $var = is_finite($var)
147:             ? ($tmp = json_encode($var)) . (strpos($tmp, '.') === FALSE ? '.0' : '')
148:             : var_export($var, TRUE);
149:         return "<span class=\"tracy-dump-number\">$var</span>\n";
150:     }
151: 
152: 
153:     private static function dumpString(& $var, $options)
154:     {
155:         return '<span class="tracy-dump-string">"'
156:             . htmlspecialchars(self::encodeString($var, $options[self::TRUNCATE]), ENT_NOQUOTES, 'UTF-8')
157:             . '"</span>' . (strlen($var) > 1 ? ' (' . strlen($var) . ')' : '') . "\n";
158:     }
159: 
160: 
161:     private static function dumpArray(& $var, $options, $level)
162:     {
163:         static $marker;
164:         if ($marker === NULL) {
165:             $marker = uniqid("\x00", TRUE);
166:         }
167: 
168:         $out = '<span class="tracy-dump-array">array</span> (';
169: 
170:         if (empty($var)) {
171:             return $out . ")\n";
172: 
173:         } elseif (isset($var[$marker])) {
174:             return $out . (count($var) - 1) . ") [ <i>RECURSION</i> ]\n";
175: 
176:         } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH]) {
177:             $collapsed = $level ? count($var) >= $options[self::COLLAPSE_COUNT] : $options[self::COLLAPSE];
178:             $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">'
179:                 . $out . count($var) . ")</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>';
180:             $var[$marker] = TRUE;
181:             foreach ($var as $k => & $v) {
182:                 if ($k !== $marker) {
183:                     $k = preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . htmlspecialchars(self::encodeString($k, $options[self::TRUNCATE]), ENT_NOQUOTES, 'UTF-8') . '"';
184:                     $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
185:                         . '<span class="tracy-dump-key">' . $k . '</span> => '
186:                         . self::dumpVar($v, $options, $level + 1);
187:                 }
188:             }
189:             unset($var[$marker]);
190:             return $out . '</div>';
191: 
192:         } else {
193:             return $out . count($var) . ") [ ... ]\n";
194:         }
195:     }
196: 
197: 
198:     private static function dumpObject(& $var, $options, $level)
199:     {
200:         if ($var instanceof \Closure) {
201:             $rc = new \ReflectionFunction($var);
202:             $fields = array();
203:             foreach ($rc->getParameters() as $param) {
204:                 $fields[] = '$' . $param->getName();
205:             }
206:             $fields = array(
207:                 'file' => $rc->getFileName(), 'line' => $rc->getStartLine(),
208:                 'variables' => $rc->getStaticVariables(), 'parameters' => implode(', ', $fields)
209:             );
210:         } elseif ($var instanceof \SplFileInfo) {
211:             $fields = array('path' => $var->getPathname());
212:         } elseif ($var instanceof \SplObjectStorage) {
213:             $fields = array();
214:             foreach (clone $var as $obj) {
215:                 $fields[] = array('object' => $obj, 'data' => $var[$obj]);
216:             }
217:         } else {
218:             $fields = (array) $var;
219:         }
220: 
221:         static $list = array();
222:         $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var);
223:         $out = '<span class="tracy-dump-object"'
224:             . ($options[self::LOCATION] && ($editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine())) ? ' data-tracy-href="' . htmlspecialchars($editor) . '"' : '')
225:             . '>' . htmlspecialchars(Helpers::getClass($var)) . '</span> <span class="tracy-dump-hash">#' . substr(md5(spl_object_hash($var)), 0, 4) . '</span>';
226: 
227:         if (empty($fields)) {
228:             return $out . "\n";
229: 
230:         } elseif (in_array($var, $list, TRUE)) {
231:             return $out . " { <i>RECURSION</i> }\n";
232: 
233:         } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH] || $var instanceof \Closure) {
234:             $collapsed = $level ? count($fields) >= $options[self::COLLAPSE_COUNT] : $options[self::COLLAPSE];
235:             $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">'
236:                 . $out . "</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>';
237:             $list[] = $var;
238:             foreach ($fields as $k => & $v) {
239:                 $vis = '';
240:                 if ($k[0] === "\x00") {
241:                     $vis = ' <span class="tracy-dump-visibility">' . ($k[1] === '*' ? 'protected' : 'private') . '</span>';
242:                     $k = substr($k, strrpos($k, "\x00") + 1);
243:                 }
244:                 $k = preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . htmlspecialchars(self::encodeString($k, $options[self::TRUNCATE]), ENT_NOQUOTES, 'UTF-8') . '"';
245:                 $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
246:                     . '<span class="tracy-dump-key">' . $k . "</span>$vis => "
247:                     . self::dumpVar($v, $options, $level + 1);
248:             }
249:             array_pop($list);
250:             return $out . '</div>';
251: 
252:         } else {
253:             return $out . " { ... }\n";
254:         }
255:     }
256: 
257: 
258:     private static function dumpResource(& $var, $options, $level)
259:     {
260:         $type = get_resource_type($var);
261:         $out = '<span class="tracy-dump-resource">' . htmlSpecialChars($type, ENT_IGNORE, 'UTF-8') . ' resource</span> '
262:             . '<span class="tracy-dump-hash">#' . intval($var) . '</span>';
263:         if (isset(self::$resources[$type])) {
264:             $out = "<span class=\"tracy-toggle tracy-collapsed\">$out</span>\n<div class=\"tracy-collapsed\">";
265:             foreach (call_user_func(self::$resources[$type], $var) as $k => $v) {
266:                 $out .= '<span class="tracy-dump-indent">   ' . str_repeat('|  ', $level) . '</span>'
267:                     . '<span class="tracy-dump-key">' . htmlSpecialChars($k, ENT_IGNORE, 'UTF-8') . '</span> => ' . self::dumpVar($v, $options, $level + 1);
268:             }
269:             return $out . '</div>';
270:         }
271:         return "$out\n";
272:     }
273: 
274: 
275:     /**
276:      * @internal
277:      * @return string UTF-8
278:      */
279:     public static function encodeString($s, $maxLength = NULL)
280:     {
281:         static $table;
282:         if ($table === NULL) {
283:             foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) {
284:                 $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
285:             }
286:             $table['\\'] = '\\\\';
287:             $table["\r"] = '\r';
288:             $table["\n"] = '\n';
289:             $table["\t"] = '\t';
290:         }
291: 
292:         if (preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $s) || preg_last_error()) {
293:             if ($maxLength && strlen($s) > $maxLength) {
294:                 $s = substr($s, 0, $maxLength) . ' ... ';
295:             }
296:             $s = strtr($s, $table);
297:         } elseif ($maxLength && strlen(utf8_decode($s)) > $maxLength) {
298:             $s = iconv_substr($s, 0, $maxLength, 'UTF-8') . ' ... ';
299:         }
300: 
301:         return $s;
302:     }
303: 
304: 
305:     /**
306:      * Finds the location where dump was called.
307:      * @return array [file, line, code]
308:      */
309:     private static function findLocation()
310:     {
311:         foreach (debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE) as $item) {
312:             if (isset($item['class']) && $item['class'] === __CLASS__) {
313:                 $location = $item;
314:                 continue;
315:             } elseif (isset($item['function'])) {
316:                 try {
317:                     $reflection = isset($item['class'])
318:                         ? new \ReflectionMethod($item['class'], $item['function'])
319:                         : new \ReflectionFunction($item['function']);
320:                     if ($reflection->isInternal() || preg_match('#\s@tracySkipLocation\s#', $reflection->getDocComment())) {
321:                         $location = $item;
322:                         continue;
323:                     }
324:                 } catch (\ReflectionException $e) {
325:                 }
326:             }
327:             break;
328:         }
329: 
330:         if (isset($location['file'], $location['line']) && is_file($location['file'])) {
331:             $lines = file($location['file']);
332:             $line = $lines[$location['line'] - 1];
333:             return array(
334:                 $location['file'],
335:                 $location['line'],
336:                 trim(preg_match('#\w*dump(er::\w+)?\(.*\)#i', $line, $m) ? $m[0] : $line),
337:             );
338:         }
339:     }
340: 
341: 
342:     /**
343:      * @return bool
344:      */
345:     private static function detectColors()
346:     {
347:         return self::$terminalColors &&
348:             (getenv('ConEmuANSI') === 'ON'
349:             || getenv('ANSICON') !== FALSE
350:             || (defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT)));
351:     }
352: 
353: }
354: 
Nette 2.2 API documentation generated by ApiGen 2.8.0