feature #31446 [VarDumper] Output the location of calls to dump() (ktherage)
This PR was merged into the 4.4 branch.
Discussion
----------
[VarDumper] Output the location of calls to dump()
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | yes
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | not tested yet
| Fixed tickets | #30830
| License | MIT
| Doc PR |
see #30830
Commits
-------
f0a59d3eab
[VarDumper] Output the location of calls to dump()
This commit is contained in:
commit
5440d671ca
|
@ -40,6 +40,19 @@
|
|||
<argument>0</argument> <!-- flags -->
|
||||
</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">
|
||||
<argument>null</argument>
|
||||
<argument>%kernel.charset%</argument>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
namespace Symfony\Component\VarDumper\Cloner;
|
||||
|
||||
use Symfony\Component\VarDumper\Caster\Caster;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
|
@ -24,6 +25,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
private $maxDepth = 20;
|
||||
private $maxItemsPerDepth = -1;
|
||||
private $useRefHandles = -1;
|
||||
private $context = [];
|
||||
|
||||
/**
|
||||
* @param array $data An array as returned by ClonerInterface::cloneVar()
|
||||
|
@ -227,6 +229,17 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
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.
|
||||
*
|
||||
|
@ -281,7 +294,18 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
public function dump(DumperInterface $dumper)
|
||||
{
|
||||
$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'])) {
|
||||
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
|
||||
}
|
||||
} elseif ($attr['if_links'] ?? false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
@ -548,6 +550,10 @@ class CliDumper extends AbstractDumper
|
|||
|
||||
protected function endValue(Cursor $cursor)
|
||||
{
|
||||
if (-1 === $cursor->hashType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
|
||||
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
|
||||
$this->line .= ',';
|
||||
|
@ -628,7 +634,7 @@ class CliDumper extends AbstractDumper
|
|||
private function getSourceLink(string $file, int $line)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
EOTXT;
|
||||
|
@ -141,6 +145,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
EOTXT;
|
||||
|
@ -309,6 +317,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
EOTXT;
|
||||
|
@ -327,7 +339,7 @@ EOTXT;
|
|||
$clone = $cloner->cloneVar($data);
|
||||
|
||||
$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]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
|
@ -372,6 +384,9 @@ object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) {
|
|||
int(-1)
|
||||
["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
||||
int(-1)
|
||||
["context":"Symfony\Component\VarDumper\Cloner\Data":private]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
|
||||
EOTXT;
|
||||
|
@ -432,6 +447,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
EOTXT;
|
||||
|
@ -501,6 +520,10 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
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\Cloner\VarCloner;
|
||||
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;
|
||||
|
||||
// Load the global dump() function
|
||||
|
@ -38,6 +40,8 @@ class VarDumper
|
|||
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper();
|
||||
}
|
||||
|
||||
$dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
|
||||
|
||||
self::$handler = function ($var) use ($cloner, $dumper) {
|
||||
$dumper->dump($cloner->cloneVar($var));
|
||||
};
|
||||
|
|
Reference in New Issue