vendor/symfony/var-dumper/Dumper/AbstractDumper.php line 45

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\VarDumper\Dumper;
  11. use Symfony\Component\VarDumper\Cloner\Data;
  12. use Symfony\Component\VarDumper\Cloner\DumperInterface;
  13. /**
  14.  * Abstract mechanism for dumping a Data object.
  15.  *
  16.  * @author Nicolas Grekas <p@tchwork.com>
  17.  */
  18. abstract class AbstractDumper implements DataDumperInterface, DumperInterface
  19. {
  20.     public const DUMP_LIGHT_ARRAY = 1;
  21.     public const DUMP_STRING_LENGTH = 2;
  22.     public const DUMP_COMMA_SEPARATOR = 4;
  23.     public const DUMP_TRAILING_COMMA = 8;
  24.     public static $defaultOutput = 'php://output';
  25.     protected $line = '';
  26.     protected $lineDumper;
  27.     protected $outputStream;
  28.     protected $decimalPoint; // This is locale dependent
  29.     protected $indentPad = '  ';
  30.     protected $flags;
  31.     private $charset = '';
  32.     /**
  33.      * @param callable|resource|string|null $output  A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput
  34.      * @param string|null                   $charset The default character encoding to use for non-UTF8 strings
  35.      * @param int                           $flags   A bit field of static::DUMP_* constants to fine tune dumps representation
  36.      */
  37.     public function __construct($output = null, string $charset = null, int $flags = 0)
  38.     {
  39.         $this->flags = $flags;
  40.         $this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8');
  41.         $this->decimalPoint = \PHP_VERSION_ID >= 80000 ? '.' : localeconv()['decimal_point'];
  42.         $this->setOutput($output ?: static::$defaultOutput);
  43.         if (!$output && \is_string(static::$defaultOutput)) {
  44.             static::$defaultOutput = $this->outputStream;
  45.         }
  46.     }
  47.     /**
  48.      * Sets the output destination of the dumps.
  49.      *
  50.      * @param callable|resource|string $output A line dumper callable, an opened stream or an output path
  51.      *
  52.      * @return callable|resource|string The previous output destination
  53.      */
  54.     public function setOutput($output)
  55.     {
  56.         $prev = $this->outputStream ?? $this->lineDumper;
  57.         if (\is_callable($output)) {
  58.             $this->outputStream = null;
  59.             $this->lineDumper = $output;
  60.         } else {
  61.             if (\is_string($output)) {
  62.                 $output = fopen($output, 'w');
  63.             }
  64.             $this->outputStream = $output;
  65.             $this->lineDumper = [$this, 'echoLine'];
  66.         }
  67.         return $prev;
  68.     }
  69.     /**
  70.      * Sets the default character encoding to use for non-UTF8 strings.
  71.      *
  72.      * @return string The previous charset
  73.      */
  74.     public function setCharset(string $charset)
  75.     {
  76.         $prev = $this->charset;
  77.         $charset = strtoupper($charset);
  78.         $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset;
  79.         $this->charset = $charset;
  80.         return $prev;
  81.     }
  82.     /**
  83.      * Sets the indentation pad string.
  84.      *
  85.      * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level
  86.      *
  87.      * @return string The previous indent pad
  88.      */
  89.     public function setIndentPad(string $pad)
  90.     {
  91.         $prev = $this->indentPad;
  92.         $this->indentPad = $pad;
  93.         return $prev;
  94.     }
  95.     /**
  96.      * Dumps a Data object.
  97.      *
  98.      * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump
  99.      *
  100.      * @return string|null The dump as string when $output is true
  101.      */
  102.     public function dump(Data $data, $output = null)
  103.     {
  104.         $this->decimalPoint = \PHP_VERSION_ID >= 80000 ? '.' : localeconv()['decimal_point'];
  105.         if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) {
  106.             setlocale(\LC_NUMERIC, 'C');
  107.         }
  108.         if ($returnDump = true === $output) {
  109.             $output = fopen('php://memory', 'r+');
  110.         }
  111.         if ($output) {
  112.             $prevOutput = $this->setOutput($output);
  113.         }
  114.         try {
  115.             $data->dump($this);
  116.             $this->dumpLine(-1);
  117.             if ($returnDump) {
  118.                 $result = stream_get_contents($output, -1, 0);
  119.                 fclose($output);
  120.                 return $result;
  121.             }
  122.         } finally {
  123.             if ($output) {
  124.                 $this->setOutput($prevOutput);
  125.             }
  126.             if ($locale) {
  127.                 setlocale(\LC_NUMERIC, $locale);
  128.             }
  129.         }
  130.         return null;
  131.     }
  132.     /**
  133.      * Dumps the current line.
  134.      *
  135.      * @param int $depth The recursive depth in the dumped structure for the line being dumped,
  136.      *                   or -1 to signal the end-of-dump to the line dumper callable
  137.      */
  138.     protected function dumpLine(int $depth)
  139.     {
  140.         ($this->lineDumper)($this->line, $depth, $this->indentPad);
  141.         $this->line = '';
  142.     }
  143.     /**
  144.      * Generic line dumper callback.
  145.      */
  146.     protected function echoLine(string $line, int $depth, string $indentPad)
  147.     {
  148.         if (-1 !== $depth) {
  149.             fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n");
  150.         }
  151.     }
  152.     /**
  153.      * Converts a non-UTF-8 string to UTF-8.
  154.      *
  155.      * @return string|null
  156.      */
  157.     protected function utf8Encode(?string $s)
  158.     {
  159.         if (null === $s || preg_match('//u', $s)) {
  160.             return $s;
  161.         }
  162.         if (!\function_exists('iconv')) {
  163.             throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
  164.         }
  165.         if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
  166.             return $c;
  167.         }
  168.         if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
  169.             return $c;
  170.         }
  171.         return iconv('CP850', 'UTF-8', $s);
  172.     }
  173. }