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