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

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