Skip to content

Commit 74b5287

Browse files
[VarDumper] Add an options builder to configure VarDumper's behaviour at runtime
1 parent 68f27ef commit 74b5287

File tree

13 files changed

+541
-88
lines changed

13 files changed

+541
-88
lines changed

.github/expected-missing-return-types.diff

Lines changed: 61 additions & 61 deletions
Large diffs are not rendered by default.

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/Bundle/DebugBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
2222
"symfony/http-kernel": "^5.4|^6.0|^7.0",
2323
"symfony/twig-bridge": "^5.4|^6.0|^7.0",
24-
"symfony/var-dumper": "^5.4|^6.0|^7.0"
24+
"symfony/var-dumper": "^6.3|^7.0"
2525
},
2626
"require-dev": {
2727
"symfony/config": "^5.4|^6.0|^7.0",

src/Symfony/Component/VarDumper/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Add support of named arguments to `dd()` and `dump()` to display the argument name
99
* Add support for `Relay\Relay`
1010
* Add display of invisible characters
11+
* Add support of options when using `dd()` and `dump()`
1112

1213
6.2
1314
---

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
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;
17+
use Symfony\Component\VarDumper\Dumper\VarDumperOptions;
1618

1719
/**
1820
* @author Nicolas Grekas <p@tchwork.com>
@@ -275,12 +277,17 @@ public function dump(DumperInterface $dumper)
275277
$cursor->hashType = -1;
276278
$cursor->attr = $this->context[SourceContextProvider::class] ?? [];
277279
$label = $this->context['label'] ?? '';
280+
$options = $this->context['options'] ?? new VarDumperOptions();
278281

279282
if ($cursor->attr || '' !== $label) {
280283
$dumper->dumpScalar($cursor, 'label', $label);
281284
}
282285
$cursor->hashType = 0;
283286
$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
287+
288+
if (false !== $options->get(VarDumperOptions::TRACE) && $cursor->attr = $this->context[BacktraceContextProvider::class] ?? []) {
289+
$this->dumpDebugBacktrace($dumper, $cursor);
290+
}
284291
}
285292

286293
/**
@@ -431,4 +438,14 @@ private function getStub(mixed $item): mixed
431438

432439
return $stub;
433440
}
441+
442+
private function dumpDebugBacktrace(DumperInterface $dumper, Cursor $cursor): void
443+
{
444+
$backtrace = $cursor->attr['backtrace'];
445+
446+
$dumper->dumpScalar($cursor, 'default', '');
447+
$dumper->dumpScalar($cursor, 'default', '**DEBUG BACKTRACE**');
448+
449+
$backtrace->dump($dumper);
450+
}
434451
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
use Symfony\Component\VarDumper\Dumper\VarDumperOptions;
18+
19+
/**
20+
* Provides the debug stacktrace of the VarDumper call.
21+
*
22+
* @author Alexandre Daubois <alex.daubois@gmail.com>
23+
*/
24+
final class BacktraceContextProvider implements ContextProviderInterface
25+
{
26+
private const BACKTRACE_CONTEXT_PROVIDER_DEPTH = 4;
27+
28+
public function __construct(
29+
private readonly bool|int $limit,
30+
private ?ClonerInterface $cloner
31+
) {
32+
$this->cloner ??= new VarCloner();
33+
}
34+
35+
public function getContext(): ?array
36+
{
37+
if (false === $this->limit) {
38+
return [];
39+
}
40+
41+
$context = [];
42+
$traces = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
43+
44+
for ($i = self::BACKTRACE_CONTEXT_PROVIDER_DEPTH; $i < \count($traces); ++$i) {
45+
if (VarDumperOptions::class === ($traces[$i + 1]['class'] ?? null)) {
46+
continue;
47+
}
48+
49+
$context[] = $traces[$i];
50+
51+
if ($this->limit === \count($context)) {
52+
break;
53+
}
54+
}
55+
56+
$stub = new TraceStub($context);
57+
58+
return ['backtrace' => $this->cloner->cloneVar($stub->value)];
59+
}
60+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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\Caster\ScalarStub;
15+
16+
/**
17+
* @author Alexandre Daubois <alex.daubois@gmail.com>
18+
*/
19+
class VarDumperOptions
20+
{
21+
public const FORMAT = '_format';
22+
public const TRACE = '_trace';
23+
public const MAX_ITEMS = '_max_items';
24+
public const MIN_DEPTH = '_min_depth';
25+
public const MAX_STRING = '_max_string';
26+
public const MAX_DEPTH = '_max_depth';
27+
public const MAX_ITEMS_PER_DEPTH = '_max_items_per_depth';
28+
public const THEME = '_theme';
29+
public const FLAGS = '_flags';
30+
public const CHARSET = '_charset';
31+
32+
public const AVAILABLE_OPTIONS = [
33+
self::FORMAT,
34+
self::TRACE,
35+
self::MAX_ITEMS,
36+
self::MIN_DEPTH,
37+
self::MAX_STRING,
38+
self::MAX_DEPTH,
39+
self::MAX_ITEMS_PER_DEPTH,
40+
self::THEME,
41+
self::FLAGS,
42+
self::CHARSET,
43+
];
44+
45+
private array $options;
46+
47+
public function __construct(array $options = [])
48+
{
49+
$this->options = array_filter(
50+
$options,
51+
static fn (mixed $key): bool => \in_array($key, self::AVAILABLE_OPTIONS),
52+
\ARRAY_FILTER_USE_KEY
53+
);
54+
}
55+
56+
/**
57+
* @template T
58+
*
59+
* @param T ...$vars
60+
*
61+
* @return T
62+
*/
63+
public function dump(mixed ...$vars): mixed
64+
{
65+
if (!$vars) {
66+
$vars = [new ScalarStub('🐛')];
67+
}
68+
69+
return dump(...$vars + $this->options);
70+
}
71+
72+
public function dd(mixed ...$vars): never
73+
{
74+
dd(...$vars + $this->options);
75+
}
76+
77+
public function format(?string $format): static
78+
{
79+
$this->options[self::FORMAT] = $format;
80+
81+
return $this;
82+
}
83+
84+
public function trace(bool|int $trace = true): static
85+
{
86+
$this->options[self::TRACE] = $trace;
87+
88+
return $this;
89+
}
90+
91+
public function maxItems(int $maxItems): static
92+
{
93+
$this->options[self::MAX_ITEMS] = $maxItems;
94+
95+
return $this;
96+
}
97+
98+
public function minDepth(int $minDepth): static
99+
{
100+
$this->options[self::MIN_DEPTH] = $minDepth;
101+
102+
return $this;
103+
}
104+
105+
public function maxString(int $maxString): static
106+
{
107+
$this->options[self::MAX_STRING] = $maxString;
108+
109+
return $this;
110+
}
111+
112+
public function maxDepth(int $maxDepth): static
113+
{
114+
$this->options[self::MAX_DEPTH] = $maxDepth;
115+
116+
return $this;
117+
}
118+
119+
public function maxItemsPerDepth(int $maxItemsPerDepth): static
120+
{
121+
$this->options[self::MAX_ITEMS_PER_DEPTH] = $maxItemsPerDepth;
122+
123+
return $this;
124+
}
125+
126+
public function theme(?string $theme): static
127+
{
128+
$this->options[self::THEME] = $theme ?? 'dark';
129+
130+
return $this;
131+
}
132+
133+
/**
134+
* @param AbstractDumper::DUMP_* $flags
135+
*/
136+
public function flags(int $flags): static
137+
{
138+
$this->options[self::FLAGS] = $flags;
139+
140+
return $this;
141+
}
142+
143+
/**
144+
* Display arrays with short form (omitting elements count and `array` prefix).
145+
*/
146+
public function showLightArray(): static
147+
{
148+
$this->options[self::FLAGS] = ($this->options[self::FLAGS] ?? 0) | AbstractDumper::DUMP_LIGHT_ARRAY;
149+
150+
return $this;
151+
}
152+
153+
/**
154+
* Display string length, just before its value.
155+
*/
156+
public function showStringLength(): static
157+
{
158+
$this->options[self::FLAGS] = ($this->options[self::FLAGS] ?? 0) | AbstractDumper::DUMP_STRING_LENGTH;
159+
160+
return $this;
161+
}
162+
163+
/**
164+
* Display a comma at the end of the line of an array element.
165+
*/
166+
public function showCommaSeparator(): static
167+
{
168+
$this->options[self::FLAGS] = ($this->options[self::FLAGS] ?? 0) | AbstractDumper::DUMP_COMMA_SEPARATOR;
169+
170+
return $this;
171+
}
172+
173+
/**
174+
* Display a trailing comma after the last element of an array.
175+
*/
176+
public function showTrailingComma(): static
177+
{
178+
$this->options[self::FLAGS] = ($this->options[self::FLAGS] ?? 0) | AbstractDumper::DUMP_TRAILING_COMMA;
179+
180+
return $this;
181+
}
182+
183+
public function charset(string $charset): static
184+
{
185+
$this->options[self::CHARSET] = $charset;
186+
187+
return $this;
188+
}
189+
190+
public function get(string $option): mixed
191+
{
192+
return $this->options[$option] ?? null;
193+
}
194+
195+
public function toArray(): array
196+
{
197+
return $this->options;
198+
}
199+
}

0 commit comments

Comments
 (0)