* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\HttpKernel\DataCollector; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Server\Connection; /** * @author Nicolas Grekas
* * @final */ class DumpDataCollector extends DataCollector implements DataDumperInterface { private string|FileLinkFormatter|false $fileLinkFormat; private int $dataCount = 0; private bool $isCollected = true; private int $clonesCount = 0; private int $clonesIndex = 0; private array $rootRefs; private string $charset; private mixed $sourceContextProvider; private bool $webMode; public function __construct( private ?Stopwatch $stopwatch = null, string|FileLinkFormatter|null $fileLinkFormat = null, ?string $charset = null, private ?RequestStack $requestStack = null, private DataDumperInterface|Connection|null $dumper = null, ?bool $webMode = null, ) { $fileLinkFormat = $fileLinkFormat ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->fileLinkFormat = $fileLinkFormat instanceof FileLinkFormatter && false === $fileLinkFormat->format('', 0) ? false : $fileLinkFormat; $this->charset = $charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8'; $this->webMode = $webMode ?? !\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true); // All clones share these properties by reference: $this->rootRefs = [ &$this->data, &$this->dataCount, &$this->isCollected, &$this->clonesCount, ]; $this->sourceContextProvider = $dumper instanceof Connection && isset($dumper->getContextProviders()['source']) ? $dumper->getContextProviders()['source'] : new SourceContextProvider($this->charset); } public function __clone() { $this->clonesIndex = ++$this->clonesCount; } public function dump(Data $data): ?string { $this->stopwatch?->start('dump'); ['name' => $name, 'file' => $file, 'line' => $line, 'file_excerpt' => $fileExcerpt] = $this->sourceContextProvider->getContext(); if (!$this->dumper || $this->dumper instanceof Connection && !$this->dumper->write($data)) { $this->isCollected = false; } $context = $data->getContext(); $label = $context['label'] ?? ''; unset($context['label']); $data = $data->withContext($context); if ($this->dumper && !$this->dumper instanceof Connection) { $this->doDump($this->dumper, $data, $name, $file, $line, $label); } if (!$this->dataCount) { $this->data = []; } $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt', 'label'); ++$this->dataCount; $this->stopwatch?->stop('dump'); return null; } public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { if (!$this->dataCount) { $this->data = []; } // Sub-requests and programmatic calls stay in the collected profile. if ($this->dumper || ($this->requestStack && $this->requestStack->getMainRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) { return; } // In all other conditions that remove the web debug toolbar, dumps are written on the output. if (!$this->requestStack || !$response->headers->has('X-Debug-Token') || $response->isRedirection() || ($response->headers->has('Content-Type') && !str_contains($response->headers->get('Content-Type') ?? '', 'html')) || 'html' !== $request->getRequestFormat() || false === strripos($response->getContent(), '