Skip to content

Commit b90f2fc

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

File tree

14 files changed

+695
-20
lines changed

14 files changed

+695
-20
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('debug.file_link_formatter')->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+
null,
121+
service('debug.file_link_formatter')->nullOnInvalid(),
122+
])
114123
],
115124
])
116125

src/Symfony/Bundle/DebugBundle/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@
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",
2828
"symfony/web-profiler-bundle": "^5.4|^6.0"
2929
},
3030
"conflict": {
3131
"symfony/config": "<5.4",
32-
"symfony/dependency-injection": "<5.4"
32+
"symfony/dependency-injection": "<5.4",
33+
"symfony/var-dumper": "<6.3"
3334
},
3435
"suggest": {
3536
"symfony/config": "For service container configuration",

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 dumper's behavior configuration with the special `_options` named argument for `dump` and `dd`
910

1011
6.2
1112
---

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

Lines changed: 29 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 (true === $options->get(VarDumperOptions::TRACE) && $cursor->attr = $this->context[BacktraceContextProvider::class] ?? []) {
287+
$this->dumpDebugBacktrace($dumper, $cursor);
288+
}
282289
}
283290

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

430437
return $stub;
431438
}
439+
440+
private function dumpDebugBacktrace(DumperInterface $dumper, Cursor $cursor): void
441+
{
442+
$traces = $cursor->attr;
443+
$dumper->dumpScalar($cursor, 'default', '');
444+
$dumper->enterHash($cursor, Cursor::HASH_OBJECT, '**DEBUG BACKTRACE**', true);
445+
446+
foreach ($traces as $trace) {
447+
$traceCursor = new Cursor();
448+
$traceCursor->attr = $trace;
449+
450+
$cursor->hashType = -1;
451+
452+
$traceCursor->attr['if_links'] = true;
453+
$dumper->dumpScalar($cursor, 'default', ' ');
454+
$dumper->dumpScalar($traceCursor, 'default', \sprintf("%s:%s", $trace['file'], $trace['line']));
455+
456+
$cursor->hashType = 0;
457+
}
458+
459+
$dumper->leaveHash($cursor, Cursor::HASH_OBJECT, '**DEBUG BACKTRACE**', true, 0);
460+
}
432461
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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\HttpKernel\Debug\FileLinkFormatter;
15+
16+
/**
17+
* Provides the debug stacktrace of the VarDumper call.
18+
*
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*/
21+
final class BacktraceContextProvider implements ContextProviderInterface
22+
{
23+
private const BACKTRACE_CONTEXT_PROVIDER_DEPTH = 5;
24+
25+
public function __construct(
26+
private readonly ?int $limit = null,
27+
private readonly ?FileLinkFormatter $fileLinkFormatter = null
28+
) {
29+
}
30+
31+
public function getContext(): ?array
32+
{
33+
$context = [];
34+
$trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS);
35+
36+
for ($i = self::BACKTRACE_CONTEXT_PROVIDER_DEPTH; $i < count($trace); ++$i) {
37+
$file = $trace[$i]['file'];
38+
$line = $trace[$i]['line'];
39+
40+
$name = str_replace('\\', '/', $file);
41+
$name = substr($name, strrpos($name, '/') + 1);
42+
43+
if ($this->fileLinkFormatter) {
44+
$fileLink = $this->fileLinkFormatter->format($file, $line);
45+
}
46+
47+
$context[] = ['name' => $name, 'file' => $file, 'line' => $line, 'file_link' => $fileLink ?? null];
48+
49+
if ($this->limit === count($context)) {
50+
break;
51+
}
52+
}
53+
54+
return $context;
55+
}
56+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
/**
15+
* @author Alexandre Daubois <alex.daubois@gmail.com>
16+
*/
17+
enum HtmlDumperTheme: string
18+
{
19+
case Dark = 'dark';
20+
case Light = 'light';
21+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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 Traversable;
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 TRACE_LIMIT = '_trace_limit';
24+
public const MAX_ITEMS = '_max_items';
25+
public const MIN_DEPTH = '_min_depth';
26+
public const MAX_STRING = '_max_string';
27+
public const MAX_DEPTH = '_max_depth';
28+
public const MAX_ITEMS_PER_DEPTH = '_max_items_per_depth';
29+
public const THEME = '_theme';
30+
public const FLAGS = '_flags';
31+
public const CHARSET = '_charset';
32+
33+
private array $options = [
34+
self::FORMAT => null,
35+
self::TRACE => false,
36+
self::TRACE_LIMIT => 0,
37+
self::MAX_ITEMS => null,
38+
self::MIN_DEPTH => null,
39+
self::MAX_STRING => null,
40+
self::MAX_DEPTH => null,
41+
self::MAX_ITEMS_PER_DEPTH => null,
42+
self::THEME => HtmlDumperTheme::Dark,
43+
self::FLAGS => 0,
44+
self::CHARSET => null,
45+
];
46+
47+
public function __construct(array $options = [])
48+
{
49+
$this->options = array_replace($this->options, $options);
50+
}
51+
52+
public function format(?string $format): static
53+
{
54+
$this->options[self::FORMAT] = $format;
55+
56+
return $this;
57+
}
58+
59+
public function trace(): static
60+
{
61+
$this->options[self::TRACE] = true;
62+
63+
return $this;
64+
}
65+
66+
public function traceLimit(int $limit): static
67+
{
68+
$this->options[self::TRACE_LIMIT] = max(0, $limit);
69+
70+
return $this;
71+
}
72+
73+
public function maxItems(int $maxItems): static
74+
{
75+
$this->options[self::MAX_ITEMS] = $maxItems;
76+
77+
return $this;
78+
}
79+
80+
public function minDepth(int $minDepth): static
81+
{
82+
$this->options[self::MIN_DEPTH] = $minDepth;
83+
84+
return $this;
85+
}
86+
87+
public function maxString(int $maxString): static
88+
{
89+
$this->options[self::MAX_STRING] = $maxString;
90+
91+
return $this;
92+
}
93+
94+
public function maxDepth(int $maxDepth): static
95+
{
96+
$this->options[self::MAX_DEPTH] = $maxDepth;
97+
98+
return $this;
99+
}
100+
101+
public function maxItemsPerDepth(int $maxItemsPerDepth): static
102+
{
103+
$this->options[self::MAX_ITEMS_PER_DEPTH] = $maxItemsPerDepth;
104+
105+
return $this;
106+
}
107+
108+
public function theme(?HtmlDumperTheme $theme): static
109+
{
110+
$this->options[self::THEME] = $theme ?? HtmlDumperTheme::Dark;
111+
112+
return $this;
113+
}
114+
115+
/**
116+
* Set flags manually. Valid flags are {@see AbstractDumper::DUMP_*} constants.
117+
*/
118+
public function flags(int $flags): static
119+
{
120+
$this->options[self::FLAGS] = $flags;
121+
122+
return $this;
123+
}
124+
125+
/**
126+
* Display arrays with short form (omitting elements count and `array` prefix).
127+
*/
128+
public function lightArray(): static
129+
{
130+
$this->options[self::FLAGS] |= AbstractDumper::DUMP_LIGHT_ARRAY;
131+
132+
return $this;
133+
}
134+
135+
/**
136+
* Display string lengths, just before its value.
137+
*/
138+
public function stringLength(): static
139+
{
140+
$this->options[self::FLAGS] |= AbstractDumper::DUMP_STRING_LENGTH;
141+
142+
return $this;
143+
}
144+
145+
/**
146+
* Display a comma at the end of the line of an array element.
147+
*/
148+
public function commaSeparator(): static
149+
{
150+
$this->options[self::FLAGS] |= AbstractDumper::DUMP_COMMA_SEPARATOR;
151+
152+
return $this;
153+
}
154+
155+
/**
156+
* Display a trailing comma after the last element of an array.
157+
*/
158+
public function trailingComma(): static
159+
{
160+
$this->options[self::FLAGS] |= AbstractDumper::DUMP_TRAILING_COMMA;
161+
162+
return $this;
163+
}
164+
165+
public function charset(string $charset): static
166+
{
167+
$this->options[self::CHARSET] = $charset;
168+
169+
return $this;
170+
}
171+
172+
public function get(string $option): mixed
173+
{
174+
return $this->options[$option] ?? null;
175+
}
176+
177+
public function getOptions(): array
178+
{
179+
return $this->options;
180+
}
181+
182+
public function set(string $option, mixed $value): static
183+
{
184+
$this->options[$option] = $value;
185+
186+
return $this;
187+
}
188+
}

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