Skip to content

Commit e1a90f4

Browse files
[VarDumper] Add an options builder to configure VarDumper's behaviour at runtime
1 parent 99b25a1 commit e1a90f4

File tree

9 files changed

+298
-26
lines changed

9 files changed

+298
-26
lines changed

src/Symfony/Bundle/DebugBundle/Resources/config/services.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
2323
use Symfony\Component\VarDumper\Command\ServerDumpCommand;
2424
use Symfony\Component\VarDumper\Dumper\CliDumper;
25+
use Symfony\Component\VarDumper\Dumper\ContextProvider\BacktraceContextProvider;
2526
use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider;
2627
use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider;
2728
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
@@ -87,6 +88,10 @@
8788
param('kernel.project_dir'),
8889
service('debug.file_link_formatter')->nullOnInvalid(),
8990
]),
91+
'backtrace' => inline_service(BacktraceContextProvider::class)->args([
92+
0,
93+
service('var_dump.cloner')->nullOnInvalid(),
94+
]),
9095
],
9196
])
9297

@@ -111,6 +116,10 @@
111116
]),
112117
'request' => inline_service(RequestContextProvider::class)->args([service('request_stack')]),
113118
'cli' => inline_service(CliContextProvider::class),
119+
'backtrace' => inline_service(BacktraceContextProvider::class)->args([
120+
0,
121+
service('var_dump.cloner')->nullOnInvalid(),
122+
]),
114123
],
115124
])
116125

src/Symfony/Component/VarDumper/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Add support for `FORCE_COLOR` environment variable
8+
* Add support of options when using `dd()` and `dump()`
89

910
7.1
1011
---

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

Lines changed: 16 additions & 0 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\BacktraceContextProvider;
1516
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
1617

1718
/**
@@ -270,12 +271,17 @@ public function dump(DumperInterface $dumper): void
270271
$cursor->hashType = -1;
271272
$cursor->attr = $this->context[SourceContextProvider::class] ?? [];
272273
$label = $this->context['label'] ?? '';
274+
$options = $this->context['options'] ?? [];
273275

274276
if ($cursor->attr || '' !== $label) {
275277
$dumper->dumpScalar($cursor, 'label', $label);
276278
}
277279
$cursor->hashType = 0;
278280
$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
281+
282+
if (false !== ($options['_trace'] ?? false) && $cursor->attr = $this->context[BacktraceContextProvider::class] ?? []) {
283+
$this->dumpDebugBacktrace($dumper, $cursor);
284+
}
279285
}
280286

281287
/**
@@ -426,4 +432,14 @@ private function getStub(mixed $item): mixed
426432

427433
return $stub;
428434
}
435+
436+
private function dumpDebugBacktrace(DumperInterface $dumper, Cursor $cursor): void
437+
{
438+
$backtrace = $cursor->attr['backtrace'];
439+
440+
$dumper->dumpScalar($cursor, 'default', '');
441+
$dumper->dumpScalar($cursor, 'default', '**DEBUG BACKTRACE**');
442+
443+
$backtrace->dump($dumper);
444+
}
429445
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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\ContextProvider;
13+
14+
use Symfony\Component\VarDumper\Caster\TraceStub;
15+
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
16+
use Symfony\Component\VarDumper\Cloner\VarCloner;
17+
18+
/**
19+
* Provides the debug stacktrace of the VarDumper call.
20+
*
21+
* @author Alexandre Daubois <alex.daubois@gmail.com>
22+
*/
23+
final class BacktraceContextProvider implements ContextProviderInterface
24+
{
25+
private const BACKTRACE_CONTEXT_PROVIDER_DEPTH = 4;
26+
27+
public function __construct(
28+
private readonly bool|int $limit,
29+
private ?ClonerInterface $cloner,
30+
) {
31+
$this->cloner ??= new VarCloner();
32+
}
33+
34+
public function getContext(): ?array
35+
{
36+
if (false === $this->limit) {
37+
return [];
38+
}
39+
40+
$context = [];
41+
$traces = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
42+
43+
for ($i = self::BACKTRACE_CONTEXT_PROVIDER_DEPTH; $i < \count($traces); ++$i) {
44+
$context[] = $traces[$i];
45+
46+
if ($this->limit === \count($context)) {
47+
break;
48+
}
49+
}
50+
51+
$stub = new TraceStub($context);
52+
53+
return ['backtrace' => $this->cloner->cloneVar($stub->value)];
54+
}
55+
}

src/Symfony/Component/VarDumper/Resources/functions/dump.php

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,33 @@
1919
*/
2020
function dump(mixed ...$vars): mixed
2121
{
22-
if (!$vars) {
23-
VarDumper::dump(new ScalarStub('🐛'));
22+
$options = array_filter(
23+
$vars,
24+
static fn (mixed $key): bool => \in_array($key, VarDumper::AVAILABLE_OPTIONS, true),
25+
\ARRAY_FILTER_USE_KEY
26+
);
2427

25-
return null;
26-
}
28+
$trace = $options['_trace'] ?? null;
29+
unset($options['_trace']);
2730

2831
if (array_key_exists(0, $vars) && 1 === count($vars)) {
29-
VarDumper::dump($vars[0]);
32+
VarDumper::dump($vars[0], null, $options);
3033
$k = 0;
3134
} else {
35+
$vars = array_filter($vars, static fn (int|string $key) => !str_starts_with($key, '_'), \ARRAY_FILTER_USE_KEY);
36+
37+
if (!$vars) {
38+
VarDumper::dump(new ScalarStub('🐛'), null, $options);
39+
40+
return null;
41+
}
42+
3243
foreach ($vars as $k => $v) {
33-
VarDumper::dump($v, is_int($k) ? 1 + $k : $k);
44+
if (array_key_last($vars) === $k) {
45+
$options['_trace'] = $trace;
46+
}
47+
48+
VarDumper::dump($v, is_int($k) ? 1 + $k : $k, $options);
3449
}
3550
}
3651

@@ -55,11 +70,21 @@ function dd(mixed ...$vars): never
5570
exit(1);
5671
}
5772

73+
$options = array_filter(
74+
$vars,
75+
static fn (mixed $key): bool => \in_array($key, VarDumper::AVAILABLE_OPTIONS, true),
76+
\ARRAY_FILTER_USE_KEY
77+
);
78+
5879
if (array_key_exists(0, $vars) && 1 === count($vars)) {
59-
VarDumper::dump($vars[0]);
80+
VarDumper::dump($vars[0], null, $options);
6081
} else {
6182
foreach ($vars as $k => $v) {
62-
VarDumper::dump($v, is_int($k) ? 1 + $k : $k);
83+
if (str_starts_with($k, '_')) {
84+
continue;
85+
}
86+
87+
VarDumper::dump($v, is_int($k) ? 1 + $k : $k, $options);
6388
}
6489
}
6590

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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\ContextProvider;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\VarDumper\Cloner\VarCloner;
16+
use Symfony\Component\VarDumper\Dumper\ContextProvider\BacktraceContextProvider;
17+
18+
class BacktraceContextProviderTest extends TestCase
19+
{
20+
public function testFalseBacktraceLimit()
21+
{
22+
$provider = new BacktraceContextProvider(false, new VarCloner());
23+
$this->assertArrayNotHasKey('backtrace', $provider->getContext());
24+
}
25+
26+
public function testPositiveBacktraceLimit()
27+
{
28+
$provider = new BacktraceContextProvider(2, new VarCloner());
29+
$this->assertCount(2, $provider->getContext()['backtrace']);
30+
}
31+
}

src/Symfony/Component/VarDumper/Tests/Dumper/ContextualizedDumperTest.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
namespace Symfony\Component\VarDumper\Tests\Dumper;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
1516
use Symfony\Component\VarDumper\Cloner\VarCloner;
1617
use Symfony\Component\VarDumper\Dumper\CliDumper;
18+
use Symfony\Component\VarDumper\Dumper\ContextProvider\BacktraceContextProvider;
1719
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
1820
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
1921

2022
/**
2123
* @author Kévin Thérage <therage.kevin@gmail.com>
24+
* @author Alexandre Daubois <alex.daubois@gmail.com>
2225
*/
2326
class ContextualizedDumperTest extends TestCase
2427
{
@@ -28,8 +31,8 @@ public function testContextualizedCliDumper()
2831
$wrappedDumper->setColors(true);
2932

3033
$var = 'example';
31-
$href = \sprintf('file://%s#L%s', __FILE__, 37);
32-
$dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider()]);
34+
$href = \sprintf('file://%s#L%s', __FILE__, 40);
35+
$dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider(fileLinkFormatter: new FileLinkFormatter())]);
3336
$cloner = new VarCloner();
3437
$data = $cloner->cloneVar($var);
3538

@@ -40,4 +43,22 @@ public function testContextualizedCliDumper()
4043
$this->assertStringContainsString("\e]8;;{$href}\e\\^\e]", $out);
4144
$this->assertStringContainsString("m{$var}\e[", $out);
4245
}
46+
47+
public function testEnablingBacktraceDisplaysIt()
48+
{
49+
$wrappedDumper = new CliDumper('php://output');
50+
$cloner = new VarCloner();
51+
52+
$dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider(), new BacktraceContextProvider(0, $cloner)]);
53+
54+
ob_start();
55+
$dumper->dump(
56+
$cloner->cloneVar(123)->withContext([
57+
'options' => ['_trace' => true],
58+
])
59+
);
60+
$result = ob_get_clean();
61+
62+
$this->assertStringContainsString('**DEBUG BACKTRACE**', $result);
63+
}
4364
}

src/Symfony/Component/VarDumper/Tests/Dumper/FunctionsTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ public function testDumpReturnsAllNamedArgsInArray()
8585
$this->assertSame([$var1, 'second' => $var2, 'third' => $var3], $return);
8686
}
8787

88+
public function testDumpReturnsAllNamedArgsExceptSpecialOptionOnes()
89+
{
90+
$this->setupVarDumper();
91+
92+
$var1 = 'a';
93+
$var2 = 'b';
94+
95+
ob_start();
96+
$return = dump($var1, named: $var2, _trace: true, _flags: 0);
97+
ob_end_clean();
98+
99+
$this->assertSame([$var1, 'named' => $var2], $return);
100+
}
101+
88102
protected function setupVarDumper()
89103
{
90104
$cloner = new VarCloner();

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