vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php line 103

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\HttpKernel\DataCollector;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\RequestStack;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
  15. use Symfony\Component\Stopwatch\Stopwatch;
  16. use Symfony\Component\VarDumper\Cloner\Data;
  17. use Symfony\Component\VarDumper\Cloner\VarCloner;
  18. use Symfony\Component\VarDumper\Dumper\CliDumper;
  19. use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
  20. use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
  21. use Symfony\Component\VarDumper\Dumper\HtmlDumper;
  22. use Symfony\Component\VarDumper\Server\Connection;
  23. /**
  24.  * @author Nicolas Grekas <p@tchwork.com>
  25.  *
  26.  * @final
  27.  */
  28. class DumpDataCollector extends DataCollector implements DataDumperInterface
  29. {
  30.     private $stopwatch;
  31.     private $fileLinkFormat;
  32.     private $dataCount 0;
  33.     private $isCollected true;
  34.     private $clonesCount 0;
  35.     private $clonesIndex 0;
  36.     private $rootRefs;
  37.     private $charset;
  38.     private $requestStack;
  39.     private $dumper;
  40.     private $sourceContextProvider;
  41.     /**
  42.      * @param string|FileLinkFormatter|null       $fileLinkFormat
  43.      * @param DataDumperInterface|Connection|null $dumper
  44.      */
  45.     public function __construct(Stopwatch $stopwatch null$fileLinkFormat nullstring $charset nullRequestStack $requestStack null$dumper null)
  46.     {
  47.         $this->stopwatch $stopwatch;
  48.         $this->fileLinkFormat $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
  49.         $this->charset $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8';
  50.         $this->requestStack $requestStack;
  51.         $this->dumper $dumper;
  52.         // All clones share these properties by reference:
  53.         $this->rootRefs = [
  54.             &$this->data,
  55.             &$this->dataCount,
  56.             &$this->isCollected,
  57.             &$this->clonesCount,
  58.         ];
  59.         $this->sourceContextProvider $dumper instanceof Connection && isset($dumper->getContextProviders()['source']) ? $dumper->getContextProviders()['source'] : new SourceContextProvider($this->charset);
  60.     }
  61.     public function __clone()
  62.     {
  63.         $this->clonesIndex = ++$this->clonesCount;
  64.     }
  65.     public function dump(Data $data)
  66.     {
  67.         if ($this->stopwatch) {
  68.             $this->stopwatch->start('dump');
  69.         }
  70.         ['name' => $name'file' => $file'line' => $line'file_excerpt' => $fileExcerpt] = $this->sourceContextProvider->getContext();
  71.         if ($this->dumper instanceof Connection) {
  72.             if (!$this->dumper->write($data)) {
  73.                 $this->isCollected false;
  74.             }
  75.         } elseif ($this->dumper) {
  76.             $this->doDump($this->dumper$data$name$file$line);
  77.         } else {
  78.             $this->isCollected false;
  79.         }
  80.         if (!$this->dataCount) {
  81.             $this->data = [];
  82.         }
  83.         $this->data[] = compact('data''name''file''line''fileExcerpt');
  84.         ++$this->dataCount;
  85.         if ($this->stopwatch) {
  86.             $this->stopwatch->stop('dump');
  87.         }
  88.     }
  89.     public function collect(Request $requestResponse $response\Throwable $exception null)
  90.     {
  91.         if (!$this->dataCount) {
  92.             $this->data = [];
  93.         }
  94.         // Sub-requests and programmatic calls stay in the collected profile.
  95.         if ($this->dumper || ($this->requestStack && $this->requestStack->getMainRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) {
  96.             return;
  97.         }
  98.         // In all other conditions that remove the web debug toolbar, dumps are written on the output.
  99.         if (!$this->requestStack
  100.             || !$response->headers->has('X-Debug-Token')
  101.             || $response->isRedirection()
  102.             || ($response->headers->has('Content-Type') && !str_contains($response->headers->get('Content-Type'), 'html'))
  103.             || 'html' !== $request->getRequestFormat()
  104.             || false === strripos($response->getContent(), '</body>')
  105.         ) {
  106.             if ($response->headers->has('Content-Type') && str_contains($response->headers->get('Content-Type'), 'html')) {
  107.                 $dumper = new HtmlDumper('php://output'$this->charset);
  108.                 $dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
  109.             } else {
  110.                 $dumper = new CliDumper('php://output'$this->charset);
  111.                 if (method_exists($dumper'setDisplayOptions')) {
  112.                     $dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
  113.                 }
  114.             }
  115.             foreach ($this->data as $dump) {
  116.                 $this->doDump($dumper$dump['data'], $dump['name'], $dump['file'], $dump['line']);
  117.             }
  118.         }
  119.     }
  120.     public function reset()
  121.     {
  122.         if ($this->stopwatch) {
  123.             $this->stopwatch->reset();
  124.         }
  125.         $this->data = [];
  126.         $this->dataCount 0;
  127.         $this->isCollected true;
  128.         $this->clonesCount 0;
  129.         $this->clonesIndex 0;
  130.     }
  131.     /**
  132.      * @internal
  133.      */
  134.     public function __sleep(): array
  135.     {
  136.         if (!$this->dataCount) {
  137.             $this->data = [];
  138.         }
  139.         if ($this->clonesCount !== $this->clonesIndex) {
  140.             return [];
  141.         }
  142.         $this->data[] = $this->fileLinkFormat;
  143.         $this->data[] = $this->charset;
  144.         $this->dataCount 0;
  145.         $this->isCollected true;
  146.         return parent::__sleep();
  147.     }
  148.     /**
  149.      * @internal
  150.      */
  151.     public function __wakeup()
  152.     {
  153.         parent::__wakeup();
  154.         $charset array_pop($this->data);
  155.         $fileLinkFormat array_pop($this->data);
  156.         $this->dataCount \count($this->data);
  157.         foreach ($this->data as $dump) {
  158.             if (!\is_string($dump['name']) || !\is_string($dump['file']) || !\is_int($dump['line'])) {
  159.                 throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  160.             }
  161.         }
  162.         self::__construct($this->stopwatch\is_string($fileLinkFormat) || $fileLinkFormat instanceof FileLinkFormatter $fileLinkFormat null\is_string($charset) ? $charset null);
  163.     }
  164.     public function getDumpsCount(): int
  165.     {
  166.         return $this->dataCount;
  167.     }
  168.     public function getDumps(string $formatint $maxDepthLimit = -1int $maxItemsPerDepth = -1): array
  169.     {
  170.         $data fopen('php://memory''r+');
  171.         if ('html' === $format) {
  172.             $dumper = new HtmlDumper($data$this->charset);
  173.             $dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
  174.         } else {
  175.             throw new \InvalidArgumentException(sprintf('Invalid dump format: "%s".'$format));
  176.         }
  177.         $dumps = [];
  178.         if (!$this->dataCount) {
  179.             return $this->data = [];
  180.         }
  181.         foreach ($this->data as $dump) {
  182.             $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth));
  183.             $dump['data'] = stream_get_contents($data, -10);
  184.             ftruncate($data0);
  185.             rewind($data);
  186.             $dumps[] = $dump;
  187.         }
  188.         return $dumps;
  189.     }
  190.     public function getName(): string
  191.     {
  192.         return 'dump';
  193.     }
  194.     public function __destruct()
  195.     {
  196.         if (=== $this->clonesCount-- && !$this->isCollected && $this->dataCount) {
  197.             $this->clonesCount 0;
  198.             $this->isCollected true;
  199.             $h headers_list();
  200.             $i \count($h);
  201.             array_unshift($h'Content-Type: '.ini_get('default_mimetype'));
  202.             while (!== stripos($h[$i], 'Content-Type:')) {
  203.                 --$i;
  204.             }
  205.             if (!\in_array(\PHP_SAPI, ['cli''phpdbg'], true) && stripos($h[$i], 'html')) {
  206.                 $dumper = new HtmlDumper('php://output'$this->charset);
  207.                 $dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
  208.             } else {
  209.                 $dumper = new CliDumper('php://output'$this->charset);
  210.                 if (method_exists($dumper'setDisplayOptions')) {
  211.                     $dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
  212.                 }
  213.             }
  214.             foreach ($this->data as $i => $dump) {
  215.                 $this->data[$i] = null;
  216.                 $this->doDump($dumper$dump['data'], $dump['name'], $dump['file'], $dump['line']);
  217.             }
  218.             $this->data = [];
  219.             $this->dataCount 0;
  220.         }
  221.     }
  222.     private function doDump(DataDumperInterface $dumperData $datastring $namestring $fileint $line)
  223.     {
  224.         if ($dumper instanceof CliDumper) {
  225.             $contextDumper = function ($name$file$line$fmt) {
  226.                 if ($this instanceof HtmlDumper) {
  227.                     if ($file) {
  228.                         $s $this->style('meta''%s');
  229.                         $f strip_tags($this->style(''$file));
  230.                         $name strip_tags($this->style(''$name));
  231.                         if ($fmt && $link \is_string($fmt) ? strtr($fmt, ['%f' => $file'%l' => $line]) : $fmt->format($file$line)) {
  232.                             $name sprintf('<a href="%s" title="%s">'.$s.'</a>'strip_tags($this->style(''$link)), $f$name);
  233.                         } else {
  234.                             $name sprintf('<abbr title="%s">'.$s.'</abbr>'$f$name);
  235.                         }
  236.                     } else {
  237.                         $name $this->style('meta'$name);
  238.                     }
  239.                     $this->line $name.' on line '.$this->style('meta'$line).':';
  240.                 } else {
  241.                     $this->line $this->style('meta'$name).' on line '.$this->style('meta'$line).':';
  242.                 }
  243.                 $this->dumpLine(0);
  244.             };
  245.             $contextDumper $contextDumper->bindTo($dumper$dumper);
  246.             $contextDumper($name$file$line$this->fileLinkFormat);
  247.         } else {
  248.             $cloner = new VarCloner();
  249.             $dumper->dump($cloner->cloneVar($name.' on line '.$line.':'));
  250.         }
  251.         $dumper->dump($data);
  252.     }
  253. }