Skip to content

Commit a596b1c

Browse files
[VarDumper] Add an options builder to configure VarDumper's behaviour at runtime
1 parent d8d93c6 commit a596b1c

File tree

11 files changed

+466
-24
lines changed

11 files changed

+466
-24
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+
null,
93+
service('var_dump.cloner'),
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+
null,
121+
service('var_dump.cloner'),
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",
2222
"symfony/http-kernel": "^5.4|^6.0",
2323
"symfony/twig-bridge": "^5.4|^6.0",
24-
"symfony/var-dumper": "^5.4|^6.0"
24+
"symfony/var-dumper": "^6.3"
2525
},
2626
"require-dev": {
2727
"symfony/config": "^5.4|^6.0",

src/Symfony/Component/VarDumper/CHANGELOG.md

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

77
* Add caster for `WeakMap`
88
* Add support of named arguments to `dd()` and `dump()` to display the argument name
9+
* Add support of options when using `dd()` and `dump()`
910

1011
6.2
1112
---

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>
@@ -268,6 +270,7 @@ public function dump(DumperInterface $dumper)
268270
$refs = [0];
269271
$cursor = new Cursor();
270272
$label = $this->context['label'] ?? '';
273+
$options = $this->context['options'] ?? new VarDumperOptions();
271274

272275
if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) {
273276
$cursor->attr['if_links'] = true;
@@ -279,6 +282,10 @@ public function dump(DumperInterface $dumper)
279282
}
280283

281284
$this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]);
285+
286+
if (false !== $options->get(VarDumperOptions::TRACE) && $cursor->attr = $this->context[BacktraceContextProvider::class] ?? []) {
287+
$this->dumpDebugBacktrace($dumper, $cursor);
288+
}
282289
}
283290

284291
/**
@@ -429,4 +436,14 @@ private function getStub(mixed $item)
429436

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

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