Skip to content

[VarDumper] add link to source next to class names #30301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public function load(array $configs, ContainerBuilder $container)
->addMethodCall('setMinDepth', [$config['min_depth']])
->addMethodCall('setMaxString', [$config['max_string_length']]);

if (method_exists(ReflectionClass::class, 'unsetClosureFileInfo')) {
$container->getDefinition('var_dumper.cloner')
->addMethodCall('addCasters', ReflectionClass::UNSET_CLOSURE_FILE_INFO);
}

if (method_exists(HtmlDumper::class, 'setTheme') && 'dark' !== $config['theme']) {
$container->getDefinition('var_dumper.html_dumper')
->addMethodCall('setTheme', [$config['theme']]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\HttpKernel\DataCollector;

use Symfony\Component\VarDumper\Caster\CutStub;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
Expand Down Expand Up @@ -79,7 +80,7 @@ protected function cloneVar($var)
*/
protected function getCasters()
{
return [
$casters = [
'*' => function ($v, array $a, Stub $s, $isNested) {
if (!$v instanceof Stub) {
foreach ($a as $k => $v) {
Expand All @@ -92,5 +93,11 @@ protected function getCasters()
return $a;
},
];

if (method_exists(ReflectionCaster::class, 'unsetClosureFileInfo')) {
$casters += ReflectionCaster::UNSET_CLOSURE_FILE_INFO;
}

return $casters;
}
}
12 changes: 4 additions & 8 deletions src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,17 @@ public static function generateUrlFormat(UrlGeneratorInterface $router, $routeNa

private function getFileLinkFormat()
{
if ($this->fileLinkFormat) {
return $this->fileLinkFormat;
}
if ($this->requestStack && $this->baseDir && $this->urlFormat) {
$request = $this->requestStack->getMasterRequest();
if ($request instanceof Request) {
if ($this->urlFormat instanceof \Closure && !$this->urlFormat = ($this->urlFormat)()) {
return;
}

return [
if ($request instanceof Request && (!$this->urlFormat instanceof \Closure || $this->urlFormat = ($this->urlFormat)())) {
$this->fileLinkFormat = [
$request->getSchemeAndHttpHost().$request->getBasePath().$this->urlFormat,
$this->baseDir.\DIRECTORY_SEPARATOR, '',
];
}
}

return $this->fileLinkFormat;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,6 @@ public function testWhenFileLinkFormatAndNoRequest()
$this->assertSame("debug://open?url=file://$file&line=3", $sut->format($file, 3));
}

public function testWhenFileLinkFormatAndRequest()
{
$file = __DIR__.\DIRECTORY_SEPARATOR.'file.php';
$requestStack = new RequestStack();
$request = new Request();
$requestStack->push($request);

$sut = new FileLinkFormatter('debug://open?url=file://%f&line=%l', $requestStack, __DIR__, '/_profiler/open?file=%f&line=%l#line%l');

$this->assertSame("debug://open?url=file://$file&line=3", $sut->format($file, 3));
}

public function testWhenNoFileLinkFormatAndRequest()
{
$file = __DIR__.\DIRECTORY_SEPARATOR.'file.php';
Expand Down
20 changes: 17 additions & 3 deletions src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
*/
class ReflectionCaster
{
const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo'];

private static $extraMap = [
'docComment' => 'getDocComment',
'extension' => 'getExtensionName',
Expand All @@ -46,22 +48,34 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested,

$stub->class .= self::getSignature($a);

if ($f = $c->getFileName()) {
$stub->attr['file'] = $f;
$stub->attr['line'] = $c->getStartLine();
}

unset($a[$prefix.'parameters']);

if ($filter & Caster::EXCLUDE_VERBOSE) {
$stub->cut += ($c->getFileName() ? 2 : 0) + \count($a);

return [];
}

unset($a[$prefix.'parameters']);

if ($f = $c->getFileName()) {
if ($f) {
$a[$prefix.'file'] = new LinkStub($f, $c->getStartLine());
$a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine();
}

return $a;
}

public static function unsetClosureFileInfo(\Closure $c, array $a)
{
unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']);

return $a;
}

public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested)
{
if (!class_exists('ReflectionGenerator', false)) {
Expand Down
11 changes: 9 additions & 2 deletions src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ protected function castObject(Stub $stub, $isNested)
$stub->class = get_parent_class($class).'@anonymous';
}
if (isset($this->classInfo[$class])) {
list($i, $parents, $hasDebugInfo) = $this->classInfo[$class];
list($i, $parents, $hasDebugInfo, $fileInfo) = $this->classInfo[$class];
} else {
$i = 2;
$parents = [$class];
Expand All @@ -295,9 +295,16 @@ protected function castObject(Stub $stub, $isNested)
}
$parents[] = '*';

$this->classInfo[$class] = [$i, $parents, $hasDebugInfo];
$r = new \ReflectionClass($class);
$fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [
'file' => $r->getFileName(),
'line' => $r->getStartLine(),
];

$this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo];
}

$stub->attr += $fileInfo;
$a = Caster::castObject($obj, $class, $hasDebugInfo);

try {
Expand Down
21 changes: 12 additions & 9 deletions src/Symfony/Component/VarDumper/Dumper/CliDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ public function dumpString(Cursor $cursor, $str, $bin, $cut)
public function enterHash(Cursor $cursor, $type, $class, $hasChild)
{
$this->dumpKey($cursor);
$attr = $cursor->attr;

if ($this->collapseNextHash) {
$cursor->skipChildren = true;
Expand All @@ -282,11 +283,11 @@ public function enterHash(Cursor $cursor, $type, $class, $hasChild)

$class = $this->utf8Encode($class);
if (Cursor::HASH_OBJECT === $type) {
$prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
$prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).' {' : '{';
} elseif (Cursor::HASH_RESOURCE === $type) {
$prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' ');
$prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' ');
} else {
$prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '[';
$prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class, $attr).' [' : '[';
}

if ($cursor->softRefCount || 0 < $cursor->softRefHandle) {
Expand Down Expand Up @@ -454,11 +455,9 @@ protected function style($style, $value, $attr = [])
goto href;
}

$style = $this->styles[$style];

$map = static::$controlCharsMap;
$startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : '';
$endCchr = $this->colors ? "\033[m\033[{$style}m" : '';
$endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : '';
$value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) {
$s = $startCchr;
$c = $c[$i = 0];
Expand All @@ -473,7 +472,7 @@ protected function style($style, $value, $attr = [])
if ($cchrCount && "\033" === $value[0]) {
$value = substr($value, \strlen($startCchr));
} else {
$value = "\033[{$style}m".$value;
$value = "\033[{$this->styles[$style]}m".$value;
}
if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) {
$value = substr($value, 0, -\strlen($endCchr));
Expand All @@ -485,7 +484,11 @@ protected function style($style, $value, $attr = [])
href:
if ($this->colors && $this->handlesHrefGracefully) {
if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
$attr['href'] = $href;
if ('note' === $style) {
$value .= "\033]8;;{$href}\033\\^\033]8;;\033\\";
} else {
$attr['href'] = $href;
}
}
if (isset($attr['href'])) {
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
Expand Down Expand Up @@ -632,7 +635,7 @@ private function isWindowsTrueColor()
private function getSourceLink($file, $line)
{
if ($fmt = $this->displayOptions['fileLinkFormat']) {
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file);
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\VarCloner;

/**
Expand All @@ -29,6 +30,7 @@ public function __construct(RequestStack $requestStack)
$this->requestStack = $requestStack;
$this->cloner = new VarCloner();
$this->cloner->setMaxItems(0);
$this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
}

public function getContext(): ?array
Expand Down
18 changes: 14 additions & 4 deletions src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,13 +314,17 @@ function resetHighlightedNodes(root) {
}

function a(e, f) {
addEventListener(root, e, function (e) {
addEventListener(root, e, function (e, n) {
if ('A' == e.target.tagName) {
f(e.target, e);
} else if ('A' == e.target.parentNode.tagName) {
f(e.target.parentNode, e);
} else if (e.target.nextElementSibling && 'A' == e.target.nextElementSibling.tagName) {
f(e.target.nextElementSibling, e, true);
} else if ((n = e.target.nextElementSibling) && 'A' == n.tagName) {
if (!/\bsf-dump-toggle\b/.test(n.className)) {
n = n.nextElementSibling;
}

f(n, e, true);
}
});
};
Expand Down Expand Up @@ -852,7 +856,13 @@ protected function style($style, $value, $attr = [])
} elseif ('str' === $style && 1 < $attr['length']) {
$style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
} elseif ('note' === $style && false !== $c = strrpos($v, '\\')) {
return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>', $v, $style, substr($v, $c + 1));
if (isset($attr['file']) && $link = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
$link = sprintf('<a href="%s" rel="noopener noreferrer">^</a>', esc($this->utf8Encode($link)));
} else {
$link = '';
}

return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>%s', $v, $style, substr($v, $c + 1), $link);
} elseif ('protected' === $style) {
$style .= ' title="Protected property"';
} elseif ('meta' === $style && isset($attr['title'])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function testClosureCasterExcludingVerbosity()
{
$var = function &($a = 5) {};

$this->assertDumpEquals('Closure&($a = 5) { …6}', $var, Caster::EXCLUDE_VERBOSE);
$this->assertDumpEquals('Closure&($a = 5) { …5}', $var, Caster::EXCLUDE_VERBOSE);
}

public function testReflectionParameter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ public function testCaster()
[position] => 1
[attr] => Array
(
[file] => %a%eVarClonerTest.php
[line] => 20
)

)
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/VarDumper/VarDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\VarDumper;

use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
Expand All @@ -29,6 +30,7 @@ public static function dump($var)
{
if (null === self::$handler) {
$cloner = new VarCloner();
$cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);

if (isset($_SERVER['VAR_DUMPER_FORMAT'])) {
$dumper = 'html' === $_SERVER['VAR_DUMPER_FORMAT'] ? new HtmlDumper() : new CliDumper();
Expand Down