Mercurial > hg > isophonics-drupal-site
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 } |