[VarDumper] Output the location of calls to dump()
This commit is contained in:
parent
e627989089
commit
f0a59d3eab
@ -40,6 +40,19 @@
|
|||||||
<argument>0</argument> <!-- flags -->
|
<argument>0</argument> <!-- flags -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="var_dumper.contextualized_cli_dumper" class="Symfony\Component\VarDumper\Dumper\ContextualizedDumper" decorates="var_dumper.cli_dumper">
|
||||||
|
<argument type="service" id="var_dumper.contextualized_cli_dumper.inner" />
|
||||||
|
<argument type="collection">
|
||||||
|
<argument type="service" key="source">
|
||||||
|
<service class="Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider">
|
||||||
|
<argument>%kernel.charset%</argument>
|
||||||
|
<argument type="string">%kernel.project_dir%</argument>
|
||||||
|
<argument type="service" id="debug.file_link_formatter" on-invalid="null" />
|
||||||
|
</service>
|
||||||
|
</argument>
|
||||||
|
</argument>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="var_dumper.html_dumper" class="Symfony\Component\VarDumper\Dumper\HtmlDumper">
|
<service id="var_dumper.html_dumper" class="Symfony\Component\VarDumper\Dumper\HtmlDumper">
|
||||||
<argument>null</argument>
|
<argument>null</argument>
|
||||||
<argument>%kernel.charset%</argument>
|
<argument>%kernel.charset%</argument>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\VarDumper\Cloner;
|
namespace Symfony\Component\VarDumper\Cloner;
|
||||||
|
|
||||||
use Symfony\Component\VarDumper\Caster\Caster;
|
use Symfony\Component\VarDumper\Caster\Caster;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
@ -24,6 +25,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||||||
private $maxDepth = 20;
|
private $maxDepth = 20;
|
||||||
private $maxItemsPerDepth = -1;
|
private $maxItemsPerDepth = -1;
|
||||||
private $useRefHandles = -1;
|
private $useRefHandles = -1;
|
||||||
|
private $context = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $data An array as returned by ClonerInterface::cloneVar()
|
* @param array $data An array as returned by ClonerInterface::cloneVar()
|
||||||
@ -227,6 +229,17 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withContext(array $context)
|
||||||
|
{
|
||||||
|
$data = clone $this;
|
||||||
|
$data->context = $context;
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seeks to a specific key in nested data structures.
|
* Seeks to a specific key in nested data structures.
|
||||||
*
|
*
|
||||||
@ -281,7 +294,18 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||||||
public function dump(DumperInterface $dumper)
|
public function dump(DumperInterface $dumper)
|
||||||
{
|
{
|
||||||
$refs = [0];
|
$refs = [0];
|
||||||
$this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]);
|
$cursor = new Cursor();
|
||||||
|
|
||||||
|
if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) {
|
||||||
|
$cursor->attr['if_links'] = true;
|
||||||
|
$cursor->hashType = -1;
|
||||||
|
$dumper->dumpScalar($cursor, 'default', '^');
|
||||||
|
$cursor->attr = ['if_links' => true];
|
||||||
|
$dumper->dumpScalar($cursor, 'default', ' ');
|
||||||
|
$cursor->hashType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +83,7 @@ class CliDumper extends AbstractDumper
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f';
|
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -490,6 +490,8 @@ class CliDumper extends AbstractDumper
|
|||||||
if (isset($attr['href'])) {
|
if (isset($attr['href'])) {
|
||||||
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
|
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
|
||||||
}
|
}
|
||||||
|
} elseif ($attr['if_links'] ?? false) {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
@ -548,6 +550,10 @@ class CliDumper extends AbstractDumper
|
|||||||
|
|
||||||
protected function endValue(Cursor $cursor)
|
protected function endValue(Cursor $cursor)
|
||||||
{
|
{
|
||||||
|
if (-1 === $cursor->hashType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
|
if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
|
||||||
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
|
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
|
||||||
$this->line .= ',';
|
$this->line .= ',';
|
||||||
@ -628,7 +634,7 @@ class CliDumper extends AbstractDumper
|
|||||||
private function getSourceLink(string $file, int $line)
|
private function getSourceLink(string $file, int $line)
|
||||||
{
|
{
|
||||||
if ($fmt = $this->displayOptions['fileLinkFormat']) {
|
if ($fmt = $this->displayOptions['fileLinkFormat']) {
|
||||||
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file);
|
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<?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\VarDumper\Dumper;
|
||||||
|
|
||||||
|
use Symfony\Component\VarDumper\Cloner\Data;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kévin Thérage <therage.kevin@gmail.com>
|
||||||
|
*/
|
||||||
|
class ContextualizedDumper implements DataDumperInterface
|
||||||
|
{
|
||||||
|
private $wrappedDumper;
|
||||||
|
private $contextProviders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ContextProviderInterface[] $contextProviders
|
||||||
|
*/
|
||||||
|
public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders)
|
||||||
|
{
|
||||||
|
$this->wrappedDumper = $wrappedDumper;
|
||||||
|
$this->contextProviders = $contextProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dump(Data $data)
|
||||||
|
{
|
||||||
|
$context = [];
|
||||||
|
foreach ($this->contextProviders as $contextProvider) {
|
||||||
|
$context[\get_class($contextProvider)] = $contextProvider->getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->wrappedDumper->dump($data->withContext($context));
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
@ -141,6 +145,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
@ -309,6 +317,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
@ -327,7 +339,7 @@ EOTXT;
|
|||||||
$clone = $cloner->cloneVar($data);
|
$clone = $cloner->cloneVar($data);
|
||||||
|
|
||||||
$expected = <<<'EOTXT'
|
$expected = <<<'EOTXT'
|
||||||
object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) {
|
object(Symfony\Component\VarDumper\Cloner\Data)#%d (7) {
|
||||||
["data":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
["data":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
||||||
array(2) {
|
array(2) {
|
||||||
[0]=>
|
[0]=>
|
||||||
@ -372,6 +384,9 @@ object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) {
|
|||||||
int(-1)
|
int(-1)
|
||||||
["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
||||||
int(-1)
|
int(-1)
|
||||||
|
["context":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
@ -432,6 +447,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
@ -501,6 +520,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<?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\VarDumper\Tests\Dumper;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kévin Thérage <therage.kevin@gmail.com>
|
||||||
|
*/
|
||||||
|
class ContextualizedDumperTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testContextualizedCliDumper()
|
||||||
|
{
|
||||||
|
$wrappedDumper = new CliDumper('php://output');
|
||||||
|
$wrappedDumper->setColors(true);
|
||||||
|
|
||||||
|
$var = 'example';
|
||||||
|
$href = sprintf('file://%s#L%s', __FILE__, 37);
|
||||||
|
$dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider()]);
|
||||||
|
$cloner = new VarCloner();
|
||||||
|
$data = $cloner->cloneVar($var);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$dumper->dump($data);
|
||||||
|
$out = ob_get_clean();
|
||||||
|
|
||||||
|
$this->assertStringContainsString("\e]8;;{$href}\e\\\e[", $out);
|
||||||
|
$this->assertStringContainsString("m{$var}\e[", $out);
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ namespace Symfony\Component\VarDumper;
|
|||||||
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
|
||||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||||
|
|
||||||
// Load the global dump() function
|
// Load the global dump() function
|
||||||
@ -38,6 +40,8 @@ class VarDumper
|
|||||||
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper();
|
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
|
||||||
|
|
||||||
self::$handler = function ($var) use ($cloner, $dumper) {
|
self::$handler = function ($var) use ($cloner, $dumper) {
|
||||||
$dumper->dump($cloner->cloneVar($var));
|
$dumper->dump($cloner->cloneVar($var));
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user