comparison vendor/symfony/var-dumper/Dumper/CliDumper.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\VarDumper\Dumper;
13
14 use Symfony\Component\VarDumper\Cloner\Cursor;
15 use Symfony\Component\VarDumper\Cloner\Stub;
16
17 /**
18 * CliDumper dumps variables for command line output.
19 *
20 * @author Nicolas Grekas <p@tchwork.com>
21 */
22 class CliDumper extends AbstractDumper
23 {
24 public static $defaultColors;
25 public static $defaultOutput = 'php://stdout';
26
27 protected $colors;
28 protected $maxStringWidth = 0;
29 protected $styles = array(
30 // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
31 'default' => '38;5;208',
32 'num' => '1;38;5;38',
33 'const' => '1;38;5;208',
34 'str' => '1;38;5;113',
35 'note' => '38;5;38',
36 'ref' => '38;5;247',
37 'public' => '',
38 'protected' => '',
39 'private' => '',
40 'meta' => '38;5;170',
41 'key' => '38;5;113',
42 'index' => '38;5;38',
43 );
44
45 protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/';
46 protected static $controlCharsMap = array(
47 "\t" => '\t',
48 "\n" => '\n',
49 "\v" => '\v',
50 "\f" => '\f',
51 "\r" => '\r',
52 "\033" => '\e',
53 );
54
55 /**
56 * {@inheritdoc}
57 */
58 public function __construct($output = null, $charset = null, $flags = 0)
59 {
60 parent::__construct($output, $charset, $flags);
61
62 if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) {
63 // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
64 $this->setStyles(array(
65 'default' => '31',
66 'num' => '1;34',
67 'const' => '1;31',
68 'str' => '1;32',
69 'note' => '34',
70 'ref' => '1;30',
71 'meta' => '35',
72 'key' => '32',
73 'index' => '34',
74 ));
75 }
76 }
77
78 /**
79 * Enables/disables colored output.
80 *
81 * @param bool $colors
82 */
83 public function setColors($colors)
84 {
85 $this->colors = (bool) $colors;
86 }
87
88 /**
89 * Sets the maximum number of characters per line for dumped strings.
90 *
91 * @param int $maxStringWidth
92 */
93 public function setMaxStringWidth($maxStringWidth)
94 {
95 $this->maxStringWidth = (int) $maxStringWidth;
96 }
97
98 /**
99 * Configures styles.
100 *
101 * @param array $styles A map of style names to style definitions
102 */
103 public function setStyles(array $styles)
104 {
105 $this->styles = $styles + $this->styles;
106 }
107
108 /**
109 * {@inheritdoc}
110 */
111 public function dumpScalar(Cursor $cursor, $type, $value)
112 {
113 $this->dumpKey($cursor);
114
115 $style = 'const';
116 $attr = $cursor->attr;
117
118 switch ($type) {
119 case 'default':
120 $style = 'default';
121 break;
122
123 case 'integer':
124 $style = 'num';
125 break;
126
127 case 'double':
128 $style = 'num';
129
130 switch (true) {
131 case INF === $value: $value = 'INF'; break;
132 case -INF === $value: $value = '-INF'; break;
133 case is_nan($value): $value = 'NAN'; break;
134 default:
135 $value = (string) $value;
136 if (false === strpos($value, $this->decimalPoint)) {
137 $value .= $this->decimalPoint.'0';
138 }
139 break;
140 }
141 break;
142
143 case 'NULL':
144 $value = 'null';
145 break;
146
147 case 'boolean':
148 $value = $value ? 'true' : 'false';
149 break;
150
151 default:
152 $attr += array('value' => $this->utf8Encode($value));
153 $value = $this->utf8Encode($type);
154 break;
155 }
156
157 $this->line .= $this->style($style, $value, $attr);
158
159 $this->endValue($cursor);
160 }
161
162 /**
163 * {@inheritdoc}
164 */
165 public function dumpString(Cursor $cursor, $str, $bin, $cut)
166 {
167 $this->dumpKey($cursor);
168 $attr = $cursor->attr;
169
170 if ($bin) {
171 $str = $this->utf8Encode($str);
172 }
173 if ('' === $str) {
174 $this->line .= '""';
175 $this->endValue($cursor);
176 } else {
177 $attr += array(
178 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
179 'binary' => $bin,
180 );
181 $str = explode("\n", $str);
182 if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
183 unset($str[1]);
184 $str[0] .= "\n";
185 }
186 $m = count($str) - 1;
187 $i = $lineCut = 0;
188
189 if (self::DUMP_STRING_LENGTH & $this->flags) {
190 $this->line .= '('.$attr['length'].') ';
191 }
192 if ($bin) {
193 $this->line .= 'b';
194 }
195
196 if ($m) {
197 $this->line .= '"""';
198 $this->dumpLine($cursor->depth);
199 } else {
200 $this->line .= '"';
201 }
202
203 foreach ($str as $str) {
204 if ($i < $m) {
205 $str .= "\n";
206 }
207 if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) {
208 $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8');
209 $lineCut = $len - $this->maxStringWidth;
210 }
211 if ($m && 0 < $cursor->depth) {
212 $this->line .= $this->indentPad;
213 }
214 if ('' !== $str) {
215 $this->line .= $this->style('str', $str, $attr);
216 }
217 if ($i++ == $m) {
218 if ($m) {
219 if ('' !== $str) {
220 $this->dumpLine($cursor->depth);
221 if (0 < $cursor->depth) {
222 $this->line .= $this->indentPad;
223 }
224 }
225 $this->line .= '"""';
226 } else {
227 $this->line .= '"';
228 }
229 if ($cut < 0) {
230 $this->line .= '…';
231 $lineCut = 0;
232 } elseif ($cut) {
233 $lineCut += $cut;
234 }
235 }
236 if ($lineCut) {
237 $this->line .= '…'.$lineCut;
238 $lineCut = 0;
239 }
240
241 if ($i > $m) {
242 $this->endValue($cursor);
243 } else {
244 $this->dumpLine($cursor->depth);
245 }
246 }
247 }
248 }
249
250 /**
251 * {@inheritdoc}
252 */
253 public function enterHash(Cursor $cursor, $type, $class, $hasChild)
254 {
255 $this->dumpKey($cursor);
256
257 $class = $this->utf8Encode($class);
258 if (Cursor::HASH_OBJECT === $type) {
259 $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
260 } elseif (Cursor::HASH_RESOURCE === $type) {
261 $prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' ');
262 } else {
263 $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '[';
264 }
265
266 if ($cursor->softRefCount || 0 < $cursor->softRefHandle) {
267 $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), array('count' => $cursor->softRefCount));
268 } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) {
269 $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, array('count' => $cursor->hardRefCount));
270 } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) {
271 $prefix = substr($prefix, 0, -1);
272 }
273
274 $this->line .= $prefix;
275
276 if ($hasChild) {
277 $this->dumpLine($cursor->depth);
278 }
279 }
280
281 /**
282 * {@inheritdoc}
283 */
284 public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
285 {
286 $this->dumpEllipsis($cursor, $hasChild, $cut);
287 $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
288 $this->endValue($cursor);
289 }
290
291 /**
292 * Dumps an ellipsis for cut children.
293 *
294 * @param Cursor $cursor The Cursor position in the dump
295 * @param bool $hasChild When the dump of the hash has child item
296 * @param int $cut The number of items the hash has been cut by
297 */
298 protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut)
299 {
300 if ($cut) {
301 $this->line .= ' …';
302 if (0 < $cut) {
303 $this->line .= $cut;
304 }
305 if ($hasChild) {
306 $this->dumpLine($cursor->depth + 1);
307 }
308 }
309 }
310
311 /**
312 * Dumps a key in a hash structure.
313 *
314 * @param Cursor $cursor The Cursor position in the dump
315 */
316 protected function dumpKey(Cursor $cursor)
317 {
318 if (null !== $key = $cursor->hashKey) {
319 if ($cursor->hashKeyIsBinary) {
320 $key = $this->utf8Encode($key);
321 }
322 $attr = array('binary' => $cursor->hashKeyIsBinary);
323 $bin = $cursor->hashKeyIsBinary ? 'b' : '';
324 $style = 'key';
325 switch ($cursor->hashType) {
326 default:
327 case Cursor::HASH_INDEXED:
328 if (self::DUMP_LIGHT_ARRAY & $this->flags) {
329 break;
330 }
331 $style = 'index';
332 // no break
333 case Cursor::HASH_ASSOC:
334 if (is_int($key)) {
335 $this->line .= $this->style($style, $key).' => ';
336 } else {
337 $this->line .= $bin.'"'.$this->style($style, $key).'" => ';
338 }
339 break;
340
341 case Cursor::HASH_RESOURCE:
342 $key = "\0~\0".$key;
343 // no break
344 case Cursor::HASH_OBJECT:
345 if (!isset($key[0]) || "\0" !== $key[0]) {
346 $this->line .= '+'.$bin.$this->style('public', $key).': ';
347 } elseif (0 < strpos($key, "\0", 1)) {
348 $key = explode("\0", substr($key, 1), 2);
349
350 switch ($key[0][0]) {
351 case '+': // User inserted keys
352 $attr['dynamic'] = true;
353 $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": ';
354 break 2;
355 case '~':
356 $style = 'meta';
357 if (isset($key[0][1])) {
358 parse_str(substr($key[0], 1), $attr);
359 $attr += array('binary' => $cursor->hashKeyIsBinary);
360 }
361 break;
362 case '*':
363 $style = 'protected';
364 $bin = '#'.$bin;
365 break;
366 default:
367 $attr['class'] = $key[0];
368 $style = 'private';
369 $bin = '-'.$bin;
370 break;
371 }
372
373 $this->line .= $bin.$this->style($style, $key[1], $attr).': ';
374 } else {
375 // This case should not happen
376 $this->line .= '-'.$bin.'"'.$this->style('private', $key, array('class' => '')).'": ';
377 }
378 break;
379 }
380
381 if ($cursor->hardRefTo) {
382 $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), array('count' => $cursor->hardRefCount)).' ';
383 }
384 }
385 }
386
387 /**
388 * Decorates a value with some style.
389 *
390 * @param string $style The type of style being applied
391 * @param string $value The value being styled
392 * @param array $attr Optional context information
393 *
394 * @return string The value with style decoration
395 */
396 protected function style($style, $value, $attr = array())
397 {
398 if (null === $this->colors) {
399 $this->colors = $this->supportsColors();
400 }
401
402 $style = $this->styles[$style];
403
404 $map = static::$controlCharsMap;
405 $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
406 $endCchr = $this->colors ? "\033[m\033[{$style}m" : '';
407 $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
408 $s = $startCchr;
409 $c = $c[$i = 0];
410 do {
411 $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i]));
412 } while (isset($c[++$i]));
413
414 return $s.$endCchr;
415 }, $value, -1, $cchrCount);
416
417 if ($this->colors) {
418 if ($cchrCount && "\033" === $value[0]) {
419 $value = substr($value, strlen($startCchr));
420 } else {
421 $value = "\033[{$style}m".$value;
422 }
423 if ($cchrCount && $endCchr === substr($value, -strlen($endCchr))) {
424 $value = substr($value, 0, -strlen($endCchr));
425 } else {
426 $value .= "\033[{$this->styles['default']}m";
427 }
428 }
429
430 return $value;
431 }
432
433 /**
434 * @return bool Tells if the current output stream supports ANSI colors or not
435 */
436 protected function supportsColors()
437 {
438 if ($this->outputStream !== static::$defaultOutput) {
439 return @(is_resource($this->outputStream) && function_exists('posix_isatty') && posix_isatty($this->outputStream));
440 }
441 if (null !== static::$defaultColors) {
442 return static::$defaultColors;
443 }
444 if (isset($_SERVER['argv'][1])) {
445 $colors = $_SERVER['argv'];
446 $i = count($colors);
447 while (--$i > 0) {
448 if (isset($colors[$i][5])) {
449 switch ($colors[$i]) {
450 case '--ansi':
451 case '--color':
452 case '--color=yes':
453 case '--color=force':
454 case '--color=always':
455 return static::$defaultColors = true;
456
457 case '--no-ansi':
458 case '--color=no':
459 case '--color=none':
460 case '--color=never':
461 return static::$defaultColors = false;
462 }
463 }
464 }
465 }
466
467 if ('\\' === DIRECTORY_SEPARATOR) {
468 static::$defaultColors = @(
469 '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
470 || false !== getenv('ANSICON')
471 || 'ON' === getenv('ConEmuANSI')
472 || 'xterm' === getenv('TERM')
473 );
474 } elseif (function_exists('posix_isatty')) {
475 $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null);
476 $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream;
477 static::$defaultColors = @posix_isatty($h);
478 } else {
479 static::$defaultColors = false;
480 }
481
482 return static::$defaultColors;
483 }
484
485 /**
486 * {@inheritdoc}
487 */
488 protected function dumpLine($depth, $endOfValue = false)
489 {
490 if ($this->colors) {
491 $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line);
492 }
493 parent::dumpLine($depth);
494 }
495
496 protected function endValue(Cursor $cursor)
497 {
498 if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
499 if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
500 $this->line .= ',';
501 } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) {
502 $this->line .= ',';
503 }
504 }
505
506 $this->dumpLine($cursor->depth, true);
507 }
508 }