8d7b498665
* 2.8: (28 commits) Detect Mintty for color support on Windows Detect Mintty for color support on Windows [WebProfilerBundle] Fix search button click listener [Form][Type Date/Time] added choice_translation_domain option. Massively simplifying the BC and deprecated-throwing code thanks to suggestions by stof in #15870 Making all "debug" messages use the debug router Making GuardTokenInterface extend TokenInterface Updating behavior to not continue after an authenticator has set the response Add a group for tests of the finder against the FTP server Fix trigger_error calls Fix legacy security tests tweaking message related to configuration edge case that we want to be helpful with Minor tweaks - lowering the required security-http requirement and nulling out a test field Fix license headers Fix license headers Fix license headers Ensure the ClockMock is loaded before using it in the testsuite Allow serializer 3.0 in the PropertyInfo component Add the replace rules for the security-guard component Forbid serializing a Crawler ...
296 lines
10 KiB
PHP
296 lines
10 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
*
|
|
* 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\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\HtmlDumper;
|
|
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
|
|
|
|
/**
|
|
* @author Nicolas Grekas <p@tchwork.com>
|
|
*/
|
|
class DumpDataCollector extends DataCollector implements DataDumperInterface
|
|
{
|
|
private $stopwatch;
|
|
private $fileLinkFormat;
|
|
private $dataCount = 0;
|
|
private $isCollected = true;
|
|
private $clonesCount = 0;
|
|
private $clonesIndex = 0;
|
|
private $rootRefs;
|
|
private $charset;
|
|
private $requestStack;
|
|
private $dumper;
|
|
private $dumperIsInjected;
|
|
|
|
public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null)
|
|
{
|
|
$this->stopwatch = $stopwatch;
|
|
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
|
|
$this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8';
|
|
$this->requestStack = $requestStack;
|
|
$this->dumper = $dumper;
|
|
$this->dumperIsInjected = null !== $dumper;
|
|
|
|
// All clones share these properties by reference:
|
|
$this->rootRefs = array(
|
|
&$this->data,
|
|
&$this->dataCount,
|
|
&$this->isCollected,
|
|
&$this->clonesCount,
|
|
);
|
|
}
|
|
|
|
public function __clone()
|
|
{
|
|
$this->clonesIndex = ++$this->clonesCount;
|
|
}
|
|
|
|
public function dump(Data $data)
|
|
{
|
|
if ($this->stopwatch) {
|
|
$this->stopwatch->start('dump');
|
|
}
|
|
if ($this->isCollected) {
|
|
$this->isCollected = false;
|
|
}
|
|
|
|
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 7);
|
|
|
|
$file = $trace[0]['file'];
|
|
$line = $trace[0]['line'];
|
|
$name = false;
|
|
$fileExcerpt = false;
|
|
|
|
for ($i = 1; $i < 7; ++$i) {
|
|
if (isset($trace[$i]['class'], $trace[$i]['function'])
|
|
&& 'dump' === $trace[$i]['function']
|
|
&& 'Symfony\Component\VarDumper\VarDumper' === $trace[$i]['class']
|
|
) {
|
|
$file = $trace[$i]['file'];
|
|
$line = $trace[$i]['line'];
|
|
|
|
while (++$i < 7) {
|
|
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
|
|
$file = $trace[$i]['file'];
|
|
$line = $trace[$i]['line'];
|
|
|
|
break;
|
|
} elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof \Twig_Template) {
|
|
$info = $trace[$i]['object'];
|
|
$name = $info->getTemplateName();
|
|
$src = method_exists($info, 'getSource') ? $info->getSource() : $info->getEnvironment()->getLoader()->getSource($name);
|
|
$info = $info->getDebugInfo();
|
|
if (null !== $src && isset($info[$trace[$i - 1]['line']])) {
|
|
$file = false;
|
|
$line = $info[$trace[$i - 1]['line']];
|
|
$src = explode("\n", $src);
|
|
$fileExcerpt = array();
|
|
|
|
for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) {
|
|
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
|
|
}
|
|
|
|
$fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (false === $name) {
|
|
$name = str_replace('\\', '/', $file);
|
|
$name = substr($name, strrpos($name, '/') + 1);
|
|
}
|
|
|
|
if ($this->dumper) {
|
|
$this->doDump($data, $name, $file, $line);
|
|
}
|
|
|
|
$this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt');
|
|
++$this->dataCount;
|
|
|
|
if ($this->stopwatch) {
|
|
$this->stopwatch->stop('dump');
|
|
}
|
|
}
|
|
|
|
public function collect(Request $request, Response $response, \Exception $exception = null)
|
|
{
|
|
// Sub-requests and programmatic calls stay in the collected profile.
|
|
if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $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') && false === strpos($response->headers->get('Content-Type'), 'html'))
|
|
|| 'html' !== $request->getRequestFormat()
|
|
|| false === strripos($response->getContent(), '</body>')
|
|
) {
|
|
if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) {
|
|
$this->dumper = new HtmlDumper('php://output', $this->charset);
|
|
} else {
|
|
$this->dumper = new CliDumper('php://output', $this->charset);
|
|
}
|
|
|
|
foreach ($this->data as $dump) {
|
|
$this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function serialize()
|
|
{
|
|
if ($this->clonesCount !== $this->clonesIndex) {
|
|
return 'a:0:{}';
|
|
}
|
|
|
|
$ser = serialize($this->data);
|
|
$this->data = array();
|
|
$this->dataCount = 0;
|
|
$this->isCollected = true;
|
|
if (!$this->dumperIsInjected) {
|
|
$this->dumper = null;
|
|
}
|
|
|
|
return $ser;
|
|
}
|
|
|
|
public function unserialize($data)
|
|
{
|
|
parent::unserialize($data);
|
|
$this->dataCount = count($this->data);
|
|
self::__construct($this->stopwatch);
|
|
}
|
|
|
|
public function getDumpsCount()
|
|
{
|
|
return $this->dataCount;
|
|
}
|
|
|
|
public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1)
|
|
{
|
|
$data = fopen('php://memory', 'r+b');
|
|
|
|
if ('html' === $format) {
|
|
$dumper = new HtmlDumper($data, $this->charset);
|
|
} else {
|
|
throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format));
|
|
}
|
|
$dumps = array();
|
|
|
|
foreach ($this->data as $dump) {
|
|
$dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth));
|
|
|
|
rewind($data);
|
|
$dump['data'] = stream_get_contents($data);
|
|
ftruncate($data, 0);
|
|
rewind($data);
|
|
$dumps[] = $dump;
|
|
}
|
|
|
|
return $dumps;
|
|
}
|
|
|
|
public function getName()
|
|
{
|
|
return 'dump';
|
|
}
|
|
|
|
public function __destruct()
|
|
{
|
|
if (0 === $this->clonesCount-- && !$this->isCollected && $this->data) {
|
|
$this->clonesCount = 0;
|
|
$this->isCollected = true;
|
|
|
|
$h = headers_list();
|
|
$i = count($h);
|
|
array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
|
|
while (0 !== stripos($h[$i], 'Content-Type:')) {
|
|
--$i;
|
|
}
|
|
|
|
if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
|
|
$this->dumper = new HtmlDumper('php://output', $this->charset);
|
|
} else {
|
|
$this->dumper = new CliDumper('php://output', $this->charset);
|
|
}
|
|
|
|
foreach ($this->data as $i => $dump) {
|
|
$this->data[$i] = null;
|
|
$this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
|
|
}
|
|
|
|
$this->data = array();
|
|
$this->dataCount = 0;
|
|
}
|
|
}
|
|
|
|
private function doDump($data, $name, $file, $line)
|
|
{
|
|
if ($this->dumper instanceof CliDumper) {
|
|
$contextDumper = function ($name, $file, $line, $fileLinkFormat) {
|
|
if ($this instanceof HtmlDumper) {
|
|
if ('' !== $file) {
|
|
$s = $this->style('meta', '%s');
|
|
$name = strip_tags($this->style('', $name));
|
|
$file = strip_tags($this->style('', $file));
|
|
if ($fileLinkFormat) {
|
|
$link = strtr(strip_tags($this->style('', $fileLinkFormat)), array('%f' => $file, '%l' => (int) $line));
|
|
$name = sprintf('<a href="%s" title="%s">'.$s.'</a>', $link, $file, $name);
|
|
} else {
|
|
$name = sprintf('<abbr title="%s">'.$s.'</abbr>', $file, $name);
|
|
}
|
|
} else {
|
|
$name = $this->style('meta', $name);
|
|
}
|
|
$this->line = $name.' on line '.$this->style('meta', $line).':';
|
|
} else {
|
|
$this->line = $this->style('meta', $name).' on line '.$this->style('meta', $line).':';
|
|
}
|
|
$this->dumpLine(0);
|
|
};
|
|
$contextDumper = $contextDumper->bindTo($this->dumper, $this->dumper);
|
|
$contextDumper($name, $file, $line, $this->fileLinkFormat);
|
|
} else {
|
|
$cloner = new VarCloner();
|
|
$this->dumper->dump($cloner->cloneVar($name.' on line '.$line.':'));
|
|
}
|
|
$this->dumper->dump($data);
|
|
}
|
|
|
|
private function htmlEncode($s)
|
|
{
|
|
$html = '';
|
|
|
|
$dumper = new HtmlDumper(function ($line) use (&$html) {$html .= $line;}, $this->charset);
|
|
$dumper->setDumpHeader('');
|
|
$dumper->setDumpBoundaries('', '');
|
|
|
|
$cloner = new VarCloner();
|
|
$dumper->dump($cloner->cloneVar($s));
|
|
|
|
return substr(strip_tags($html), 1, -1);
|
|
}
|
|
}
|