Skip to content

Commit dace4a0

Browse files
ktheragenicolas-grekas
authored andcommitted
[VarDumper] Output the location of calls to dump()
1 parent 0f1ad9b commit dace4a0

File tree

6 files changed

+219
-8
lines changed

6 files changed

+219
-8
lines changed

src/Symfony/Component/VarDumper/Cloner/Data.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\VarDumper\Cloner;
1313

1414
use Symfony\Component\VarDumper\Caster\Caster;
15+
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
1516

1617
/**
1718
* @author Nicolas Grekas <p@tchwork.com>
@@ -24,6 +25,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate
2425
private $maxDepth = 20;
2526
private $maxItemsPerDepth = -1;
2627
private $useRefHandles = -1;
28+
private $context = [];
2729

2830
/**
2931
* @param array $data An array as returned by ClonerInterface::cloneVar()
@@ -168,7 +170,7 @@ public function __toString()
168170
*
169171
* @param int $maxDepth The max dumped depth level
170172
*
171-
* @return self A clone of $this
173+
* @return static A clone of $this
172174
*/
173175
public function withMaxDepth($maxDepth)
174176
{
@@ -183,7 +185,7 @@ public function withMaxDepth($maxDepth)
183185
*
184186
* @param int $maxItemsPerDepth The max number of items dumped per depth level
185187
*
186-
* @return self A clone of $this
188+
* @return static A clone of $this
187189
*/
188190
public function withMaxItemsPerDepth($maxItemsPerDepth)
189191
{
@@ -198,7 +200,7 @@ public function withMaxItemsPerDepth($maxItemsPerDepth)
198200
*
199201
* @param bool $useRefHandles False to hide global ref. handles
200202
*
201-
* @return self A clone of $this
203+
* @return static A clone of $this
202204
*/
203205
public function withRefHandles($useRefHandles)
204206
{
@@ -208,12 +210,23 @@ public function withRefHandles($useRefHandles)
208210
return $data;
209211
}
210212

213+
/**
214+
* @return static A clone of $this
215+
*/
216+
public function withContext(array $context)
217+
{
218+
$data = clone $this;
219+
$data->context = $context;
220+
221+
return $data;
222+
}
223+
211224
/**
212225
* Seeks to a specific key in nested data structures.
213226
*
214227
* @param string|int $key The key to seek to
215228
*
216-
* @return self|null A clone of $this or null if the key is not set
229+
* @return static|null A clone of $this or null if the key is not set
217230
*/
218231
public function seek($key)
219232
{
@@ -262,7 +275,18 @@ public function seek($key)
262275
public function dump(DumperInterface $dumper)
263276
{
264277
$refs = [0];
265-
$this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]);
278+
$cursor = new Cursor();
279+
280+
if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) {
281+
$cursor->attr['if_links'] = true;
282+
$cursor->hashType = -1;
283+
$dumper->dumpScalar($cursor, 'default', '^');
284+
$cursor->attr = ['if_links' => true];
285+
$dumper->dumpScalar($cursor, 'default', ' ');
286+
$cursor->hashType = 0;
287+
}
288+
289+
$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
266290
}
267291

268292
/**

src/Symfony/Component/VarDumper/Dumper/CliDumper.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function __construct($output = null, string $charset = null, int $flags =
8383
]);
8484
}
8585

86-
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f';
86+
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l';
8787
}
8888

8989
/**
@@ -493,6 +493,8 @@ protected function style($style, $value, $attr = [])
493493
if (isset($attr['href'])) {
494494
$value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\";
495495
}
496+
} elseif ($attr['if_links'] ?? false) {
497+
return '';
496498
}
497499

498500
return $value;
@@ -551,6 +553,10 @@ protected function dumpLine($depth, $endOfValue = false)
551553

552554
protected function endValue(Cursor $cursor)
553555
{
556+
if (-1 === $cursor->hashType) {
557+
return;
558+
}
559+
554560
if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
555561
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
556562
$this->line .= ',';
@@ -635,7 +641,7 @@ private function isWindowsTrueColor()
635641
private function getSourceLink($file, $line)
636642
{
637643
if ($fmt = $this->displayOptions['fileLinkFormat']) {
638-
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file);
644+
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line);
639645
}
640646

641647
return false;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Dumper;
13+
14+
use Symfony\Component\VarDumper\Cloner\Data;
15+
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
16+
17+
/**
18+
* @author Kévin Thérage <therage.kevin@gmail.com>
19+
*/
20+
class ContextualizedDumper implements DataDumperInterface
21+
{
22+
private $wrappedDumper;
23+
private $contextProviders;
24+
25+
/**
26+
* @param ContextProviderInterface[] $contextProviders
27+
*/
28+
public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders)
29+
{
30+
$this->wrappedDumper = $wrappedDumper;
31+
$this->contextProviders = $contextProviders;
32+
}
33+
34+
public function dump(Data $data)
35+
{
36+
$context = [];
37+
foreach ($this->contextProviders as $contextProvider) {
38+
$context[\get_class($contextProvider)] = $contextProvider->getContext();
39+
}
40+
41+
$this->wrappedDumper->dump($data->withContext($context));
42+
}
43+
}

src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public function testMaxIntBoundary()
5252
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
5353
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
5454
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
55+
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
56+
(
57+
)
58+
5559
)
5660
5761
EOTXT;
@@ -140,6 +144,10 @@ public function testClone()
140144
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
141145
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
142146
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
147+
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
148+
(
149+
)
150+
143151
)
144152
145153
EOTXT;
@@ -308,6 +316,10 @@ public function testLimits()
308316
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
309317
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
310318
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
319+
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
320+
(
321+
)
322+
311323
)
312324
313325
EOTXT;
@@ -326,7 +338,7 @@ public function testJsonCast()
326338
$clone = $cloner->cloneVar($data);
327339

328340
$expected = <<<'EOTXT'
329-
object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) {
341+
object(Symfony\Component\VarDumper\Cloner\Data)#%d (7) {
330342
["data":"Symfony\Component\VarDumper\Cloner\Data":private]=>
331343
array(2) {
332344
[0]=>
@@ -371,6 +383,9 @@ public function testJsonCast()
371383
int(-1)
372384
["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=>
373385
int(-1)
386+
["context":"Symfony\Component\VarDumper\Cloner\Data":private]=>
387+
array(0) {
388+
}
374389
}
375390

376391
EOTXT;
@@ -431,6 +446,10 @@ public function testCaster()
431446
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
432447
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
433448
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
449+
[context:Symfony\Component\VarDumper\Cloner\Data:private] => Array
450+
(
451+
)
452+
434453
)
435454
436455
EOTXT;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\VarDumper\Tests\Dumper;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\VarDumper\Cloner\VarCloner;
16+
use Symfony\Component\VarDumper\Dumper\CliDumper;
17+
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
18+
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
19+
20+
/**
21+
* @author Kévin Thérage <therage.kevin@gmail.com>
22+
*/
23+
class ContextualizedDumperTest extends TestCase
24+
{
25+
public function testContextualizedCliDumper(): void
26+
{
27+
require __DIR__.'/../Fixtures/dumb-var.php';
28+
$wrappedDumper = new CliDumper('php://output');
29+
$wrappedDumper->setColors(false);
30+
31+
$dumper = new ContextualizedDumper($wrappedDumper, [
32+
'source_context' => new class() implements ContextProviderInterface {
33+
public function getContext(): ?array
34+
{
35+
return [
36+
'file' => '/home/example.php',
37+
'line' => 42,
38+
];
39+
}
40+
},
41+
]);
42+
$cloner = new VarCloner();
43+
$cloner->addCasters([
44+
':stream' => function ($res, $a) {
45+
unset($a['uri'], $a['wrapper_data']);
46+
47+
return $a;
48+
},
49+
]);
50+
$data = $cloner->cloneVar($var);
51+
52+
ob_start();
53+
$dumper->dump($data);
54+
$out = ob_get_clean();
55+
$out = preg_replace('/[ \t]+$/m', '', $out);
56+
$intMax = PHP_INT_MAX;
57+
$res = (int) $var['res'];
58+
59+
$this->assertStringMatchesFormat(
60+
<<<EOTXT
61+
array:24 [ ]8;;file:///home/example.php\[^]]8;;\
62+
"number" => 1
63+
0 => &1 null
64+
"const" => 1.1
65+
1 => true
66+
2 => false
67+
3 => NAN
68+
4 => INF
69+
5 => -INF
70+
6 => {$intMax}
71+
"str" => "déjà\\n"
72+
7 => b"""
73+
é\\x00test\\t\\n
74+
ing
75+
"""
76+
"[]" => []
77+
"res" => stream resource {@{$res}
78+
%A wrapper_type: "plainfile"
79+
stream_type: "STDIO"
80+
mode: "r"
81+
unread_bytes: 0
82+
seekable: true
83+
%A options: []
84+
}
85+
"obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d
86+
+foo: "foo"
87+
+"bar": "bar"
88+
}
89+
"closure" => Closure(\$a, PDO &\$b = null) {#%d
90+
class: "Symfony\Component\VarDumper\Tests\Dumper\ContextualizedDumperTest"
91+
this: Symfony\Component\VarDumper\Tests\Dumper\ContextualizedDumperTest {#%d …}
92+
file: "%s%eTests%eFixtures%edumb-var.php"
93+
line: "{$var['line']} to {$var['line']}"
94+
}
95+
"line" => {$var['line']}
96+
"nobj" => array:1 [
97+
0 => &3 {#%d}
98+
]
99+
"recurs" => &4 array:1 [
100+
0 => &4 array:1 [&4]
101+
]
102+
8 => &1 null
103+
"sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {#%d}
104+
"snobj" => &3 {#%d}
105+
"snobj2" => {#%d}
106+
"file" => "{$var['file']}"
107+
b"bin-key-é" => ""
108+
]
109+
110+
EOTXT
111+
,
112+
$out
113+
);
114+
}
115+
}

src/Symfony/Component/VarDumper/VarDumper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
1515
use Symfony\Component\VarDumper\Cloner\VarCloner;
1616
use Symfony\Component\VarDumper\Dumper\CliDumper;
17+
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
18+
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
1719
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
1820

1921
// Load the global dump() function
@@ -38,6 +40,8 @@ public static function dump($var)
3840
$dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper();
3941
}
4042

43+
$dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]);
44+
4145
self::$handler = function ($var) use ($cloner, $dumper) {
4246
$dumper->dump($cloner->cloneVar($var));
4347
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy