"Fossies" - the Fresh Open Source Software Archive

Member "grav/vendor/symfony/var-dumper/Dumper/CliDumper.php" (1 Sep 2020, 19918 Bytes) of package /linux/www/grav-v1.6.27.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CliDumper.php" see the Fossies "Dox" file reference documentation.

    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 = [
   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 = [
   47         "\t" => '\t',
   48         "\n" => '\n',
   49         "\v" => '\v',
   50         "\f" => '\f',
   51         "\r" => '\r',
   52         "\033" => '\e',
   53     ];
   54 
   55     protected $collapseNextHash = false;
   56     protected $expandNextHash = false;
   57 
   58     /**
   59      * {@inheritdoc}
   60      */
   61     public function __construct($output = null, string $charset = null, int $flags = 0)
   62     {
   63         parent::__construct($output, $charset, $flags);
   64 
   65         if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {
   66             // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
   67             $this->setStyles([
   68                 'default' => '31',
   69                 'num' => '1;34',
   70                 'const' => '1;31',
   71                 'str' => '1;32',
   72                 'note' => '34',
   73                 'ref' => '1;30',
   74                 'meta' => '35',
   75                 'key' => '32',
   76                 'index' => '34',
   77             ]);
   78         }
   79     }
   80 
   81     /**
   82      * Enables/disables colored output.
   83      *
   84      * @param bool $colors
   85      */
   86     public function setColors($colors)
   87     {
   88         $this->colors = (bool) $colors;
   89     }
   90 
   91     /**
   92      * Sets the maximum number of characters per line for dumped strings.
   93      *
   94      * @param int $maxStringWidth
   95      */
   96     public function setMaxStringWidth($maxStringWidth)
   97     {
   98         $this->maxStringWidth = (int) $maxStringWidth;
   99     }
  100 
  101     /**
  102      * Configures styles.
  103      *
  104      * @param array $styles A map of style names to style definitions
  105      */
  106     public function setStyles(array $styles)
  107     {
  108         $this->styles = $styles + $this->styles;
  109     }
  110 
  111     /**
  112      * {@inheritdoc}
  113      */
  114     public function dumpScalar(Cursor $cursor, $type, $value)
  115     {
  116         $this->dumpKey($cursor);
  117 
  118         $style = 'const';
  119         $attr = $cursor->attr;
  120 
  121         switch ($type) {
  122             case 'default':
  123                 $style = 'default';
  124                 break;
  125 
  126             case 'integer':
  127                 $style = 'num';
  128                 break;
  129 
  130             case 'double':
  131                 $style = 'num';
  132 
  133                 switch (true) {
  134                     case INF === $value:  $value = 'INF'; break;
  135                     case -INF === $value: $value = '-INF'; break;
  136                     case is_nan($value):  $value = 'NAN'; break;
  137                     default:
  138                         $value = (string) $value;
  139                         if (false === strpos($value, $this->decimalPoint)) {
  140                             $value .= $this->decimalPoint.'0';
  141                         }
  142                         break;
  143                 }
  144                 break;
  145 
  146             case 'NULL':
  147                 $value = 'null';
  148                 break;
  149 
  150             case 'boolean':
  151                 $value = $value ? 'true' : 'false';
  152                 break;
  153 
  154             default:
  155                 $attr += ['value' => $this->utf8Encode($value)];
  156                 $value = $this->utf8Encode($type);
  157                 break;
  158         }
  159 
  160         $this->line .= $this->style($style, $value, $attr);
  161 
  162         $this->endValue($cursor);
  163     }
  164 
  165     /**
  166      * {@inheritdoc}
  167      */
  168     public function dumpString(Cursor $cursor, $str, $bin, $cut)
  169     {
  170         $this->dumpKey($cursor);
  171         $attr = $cursor->attr;
  172 
  173         if ($bin) {
  174             $str = $this->utf8Encode($str);
  175         }
  176         if ('' === $str) {
  177             $this->line .= '""';
  178             $this->endValue($cursor);
  179         } else {
  180             $attr += [
  181                 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
  182                 'binary' => $bin,
  183             ];
  184             $str = explode("\n", $str);
  185             if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
  186                 unset($str[1]);
  187                 $str[0] .= "\n";
  188             }
  189             $m = \count($str) - 1;
  190             $i = $lineCut = 0;
  191 
  192             if (self::DUMP_STRING_LENGTH & $this->flags) {
  193                 $this->line .= '('.$attr['length'].') ';
  194             }
  195             if ($bin) {
  196                 $this->line .= 'b';
  197             }
  198 
  199             if ($m) {
  200                 $this->line .= '"""';
  201                 $this->dumpLine($cursor->depth);
  202             } else {
  203                 $this->line .= '"';
  204             }
  205 
  206             foreach ($str as $str) {
  207                 if ($i < $m) {
  208                     $str .= "\n";
  209                 }
  210                 if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) {
  211                     $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8');
  212                     $lineCut = $len - $this->maxStringWidth;
  213                 }
  214                 if ($m && 0 < $cursor->depth) {
  215                     $this->line .= $this->indentPad;
  216                 }
  217                 if ('' !== $str) {
  218                     $this->line .= $this->style('str', $str, $attr);
  219                 }
  220                 if ($i++ == $m) {
  221                     if ($m) {
  222                         if ('' !== $str) {
  223                             $this->dumpLine($cursor->depth);
  224                             if (0 < $cursor->depth) {
  225                                 $this->line .= $this->indentPad;
  226                             }
  227                         }
  228                         $this->line .= '"""';
  229                     } else {
  230                         $this->line .= '"';
  231                     }
  232                     if ($cut < 0) {
  233                         $this->line .= '…';
  234                         $lineCut = 0;
  235                     } elseif ($cut) {
  236                         $lineCut += $cut;
  237                     }
  238                 }
  239                 if ($lineCut) {
  240                     $this->line .= '…'.$lineCut;
  241                     $lineCut = 0;
  242                 }
  243 
  244                 if ($i > $m) {
  245                     $this->endValue($cursor);
  246                 } else {
  247                     $this->dumpLine($cursor->depth);
  248                 }
  249             }
  250         }
  251     }
  252 
  253     /**
  254      * {@inheritdoc}
  255      */
  256     public function enterHash(Cursor $cursor, $type, $class, $hasChild)
  257     {
  258         $this->dumpKey($cursor);
  259 
  260         if ($this->collapseNextHash) {
  261             $cursor->skipChildren = true;
  262             $this->collapseNextHash = $hasChild = false;
  263         }
  264 
  265         $class = $this->utf8Encode($class);
  266         if (Cursor::HASH_OBJECT === $type) {
  267             $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
  268         } elseif (Cursor::HASH_RESOURCE === $type) {
  269             $prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' ');
  270         } else {
  271             $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '[';
  272         }
  273 
  274         if ($cursor->softRefCount || 0 < $cursor->softRefHandle) {
  275             $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]);
  276         } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) {
  277             $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]);
  278         } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) {
  279             $prefix = substr($prefix, 0, -1);
  280         }
  281 
  282         $this->line .= $prefix;
  283 
  284         if ($hasChild) {
  285             $this->dumpLine($cursor->depth);
  286         }
  287     }
  288 
  289     /**
  290      * {@inheritdoc}
  291      */
  292     public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
  293     {
  294         $this->dumpEllipsis($cursor, $hasChild, $cut);
  295         $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
  296         $this->endValue($cursor);
  297     }
  298 
  299     /**
  300      * Dumps an ellipsis for cut children.
  301      *
  302      * @param Cursor $cursor   The Cursor position in the dump
  303      * @param bool   $hasChild When the dump of the hash has child item
  304      * @param int    $cut      The number of items the hash has been cut by
  305      */
  306     protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut)
  307     {
  308         if ($cut) {
  309             $this->line .= ' …';
  310             if (0 < $cut) {
  311                 $this->line .= $cut;
  312             }
  313             if ($hasChild) {
  314                 $this->dumpLine($cursor->depth + 1);
  315             }
  316         }
  317     }
  318 
  319     /**
  320      * Dumps a key in a hash structure.
  321      *
  322      * @param Cursor $cursor The Cursor position in the dump
  323      */
  324     protected function dumpKey(Cursor $cursor)
  325     {
  326         if (null !== $key = $cursor->hashKey) {
  327             if ($cursor->hashKeyIsBinary) {
  328                 $key = $this->utf8Encode($key);
  329             }
  330             $attr = ['binary' => $cursor->hashKeyIsBinary];
  331             $bin = $cursor->hashKeyIsBinary ? 'b' : '';
  332             $style = 'key';
  333             switch ($cursor->hashType) {
  334                 default:
  335                 case Cursor::HASH_INDEXED:
  336                     if (self::DUMP_LIGHT_ARRAY & $this->flags) {
  337                         break;
  338                     }
  339                     $style = 'index';
  340                     // no break
  341                 case Cursor::HASH_ASSOC:
  342                     if (\is_int($key)) {
  343                         $this->line .= $this->style($style, $key).' => ';
  344                     } else {
  345                         $this->line .= $bin.'"'.$this->style($style, $key).'" => ';
  346                     }
  347                     break;
  348 
  349                 case Cursor::HASH_RESOURCE:
  350                     $key = "\0~\0".$key;
  351                     // no break
  352                 case Cursor::HASH_OBJECT:
  353                     if (!isset($key[0]) || "\0" !== $key[0]) {
  354                         $this->line .= '+'.$bin.$this->style('public', $key).': ';
  355                     } elseif (0 < strpos($key, "\0", 1)) {
  356                         $key = explode("\0", substr($key, 1), 2);
  357 
  358                         switch ($key[0][0]) {
  359                             case '+': // User inserted keys
  360                                 $attr['dynamic'] = true;
  361                                 $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": ';
  362                                 break 2;
  363                             case '~':
  364                                 $style = 'meta';
  365                                 if (isset($key[0][1])) {
  366                                     parse_str(substr($key[0], 1), $attr);
  367                                     $attr += ['binary' => $cursor->hashKeyIsBinary];
  368                                 }
  369                                 break;
  370                             case '*':
  371                                 $style = 'protected';
  372                                 $bin = '#'.$bin;
  373                                 break;
  374                             default:
  375                                 $attr['class'] = $key[0];
  376                                 $style = 'private';
  377                                 $bin = '-'.$bin;
  378                                 break;
  379                         }
  380 
  381                         if (isset($attr['collapse'])) {
  382                             if ($attr['collapse']) {
  383                                 $this->collapseNextHash = true;
  384                             } else {
  385                                 $this->expandNextHash = true;
  386                             }
  387                         }
  388 
  389                         $this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': ');
  390                     } else {
  391                         // This case should not happen
  392                         $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": ';
  393                     }
  394                     break;
  395             }
  396 
  397             if ($cursor->hardRefTo) {
  398                 $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' ';
  399             }
  400         }
  401     }
  402 
  403     /**
  404      * Decorates a value with some style.
  405      *
  406      * @param string $style The type of style being applied
  407      * @param string $value The value being styled
  408      * @param array  $attr  Optional context information
  409      *
  410      * @return string The value with style decoration
  411      */
  412     protected function style($style, $value, $attr = [])
  413     {
  414         if (null === $this->colors) {
  415             $this->colors = $this->supportsColors();
  416         }
  417 
  418         if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {
  419             $prefix = substr($value, 0, -$attr['ellipsis']);
  420             if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) {
  421                 $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd]));
  422             }
  423             if (!empty($attr['ellipsis-tail'])) {
  424                 $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']);
  425                 $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']);
  426             } else {
  427                 $value = substr($value, -$attr['ellipsis']);
  428             }
  429 
  430             return $this->style('default', $prefix).$this->style($style, $value);
  431         }
  432 
  433         $style = $this->styles[$style];
  434 
  435         $map = static::$controlCharsMap;
  436         $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
  437         $endCchr = $this->colors ? "\033[m\033[{$style}m" : '';
  438         $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
  439             $s = $startCchr;
  440             $c = $c[$i = 0];
  441             do {
  442                 $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i]));
  443             } while (isset($c[++$i]));
  444 
  445             return $s.$endCchr;
  446         }, $value, -1, $cchrCount);
  447 
  448         if ($this->colors) {
  449             if ($cchrCount && "\033" === $value[0]) {
  450                 $value = substr($value, \strlen($startCchr));
  451             } else {
  452                 $value = "\033[{$style}m".$value;
  453             }
  454             if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) {
  455                 $value = substr($value, 0, -\strlen($endCchr));
  456             } else {
  457                 $value .= "\033[{$this->styles['default']}m";
  458             }
  459         }
  460 
  461         return $value;
  462     }
  463 
  464     /**
  465      * @return bool Tells if the current output stream supports ANSI colors or not
  466      */
  467     protected function supportsColors()
  468     {
  469         if ($this->outputStream !== static::$defaultOutput) {
  470             return $this->hasColorSupport($this->outputStream);
  471         }
  472         if (null !== static::$defaultColors) {
  473             return static::$defaultColors;
  474         }
  475         if (isset($_SERVER['argv'][1])) {
  476             $colors = $_SERVER['argv'];
  477             $i = \count($colors);
  478             while (--$i > 0) {
  479                 if (isset($colors[$i][5])) {
  480                     switch ($colors[$i]) {
  481                         case '--ansi':
  482                         case '--color':
  483                         case '--color=yes':
  484                         case '--color=force':
  485                         case '--color=always':
  486                             return static::$defaultColors = true;
  487 
  488                         case '--no-ansi':
  489                         case '--color=no':
  490                         case '--color=none':
  491                         case '--color=never':
  492                             return static::$defaultColors = false;
  493                     }
  494                 }
  495             }
  496         }
  497 
  498         $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null];
  499         $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream;
  500 
  501         return static::$defaultColors = $this->hasColorSupport($h);
  502     }
  503 
  504     /**
  505      * {@inheritdoc}
  506      */
  507     protected function dumpLine($depth, $endOfValue = false)
  508     {
  509         if ($this->colors) {
  510             $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line);
  511         }
  512         parent::dumpLine($depth);
  513     }
  514 
  515     protected function endValue(Cursor $cursor)
  516     {
  517         if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
  518             if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
  519                 $this->line .= ',';
  520             } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) {
  521                 $this->line .= ',';
  522             }
  523         }
  524 
  525         $this->dumpLine($cursor->depth, true);
  526     }
  527 
  528     /**
  529      * Returns true if the stream supports colorization.
  530      *
  531      * Reference: Composer\XdebugHandler\Process::supportsColor
  532      * https://github.com/composer/xdebug-handler
  533      *
  534      * @param mixed $stream A CLI output stream
  535      *
  536      * @return bool
  537      */
  538     private function hasColorSupport($stream)
  539     {
  540         if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
  541             return false;
  542         }
  543 
  544         if ('Hyper' === getenv('TERM_PROGRAM')) {
  545             return true;
  546         }
  547 
  548         if (\DIRECTORY_SEPARATOR === '\\') {
  549             return (\function_exists('sapi_windows_vt100_support')
  550                 && @sapi_windows_vt100_support($stream))
  551                 || false !== getenv('ANSICON')
  552                 || 'ON' === getenv('ConEmuANSI')
  553                 || 'xterm' === getenv('TERM');
  554         }
  555 
  556         if (\function_exists('stream_isatty')) {
  557             return @stream_isatty($stream);
  558         }
  559 
  560         if (\function_exists('posix_isatty')) {
  561             return @posix_isatty($stream);
  562         }
  563 
  564         $stat = @fstat($stream);
  565         // Check if formatted mode is S_IFCHR
  566         return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
  567     }
  568 
  569     /**
  570      * Returns true if the Windows terminal supports true color.
  571      *
  572      * Note that this does not check an output stream, but relies on environment
  573      * variables from known implementations, or a PHP and Windows version that
  574      * supports true color.
  575      *
  576      * @return bool
  577      */
  578     private function isWindowsTrueColor()
  579     {
  580         $result = 183 <= getenv('ANSICON_VER')
  581             || 'ON' === getenv('ConEmuANSI')
  582             || 'xterm' === getenv('TERM')
  583             || 'Hyper' === getenv('TERM_PROGRAM');
  584 
  585         if (!$result && \PHP_VERSION_ID >= 70200) {
  586             $version = sprintf(
  587                 '%s.%s.%s',
  588                 PHP_WINDOWS_VERSION_MAJOR,
  589                 PHP_WINDOWS_VERSION_MINOR,
  590                 PHP_WINDOWS_VERSION_BUILD
  591             );
  592             $result = $version >= '10.0.15063';
  593         }
  594 
  595         return $result;
  596     }
  597 }