Skip to content

Commit a94b07a

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

File tree

9 files changed

+304
-26
lines changed

9 files changed

+304
-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: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,42 @@
1616
/**
1717
* @author Nicolas Grekas <p@tchwork.com>
1818
* @author Alexandre Daubois <alex.daubois@gmail.com>
19+
*
20+
* @template T
21+
*
22+
* @param T ...$vars
23+
*
24+
* @return T
1925
*/
2026
function dump(mixed ...$vars): mixed
2127
{
22-
if (!$vars) {
23-
VarDumper::dump(new ScalarStub('🐛'));
28+
$options = array_filter(
29+
$vars,
30+
static fn (mixed $key): bool => in_array($key, VarDumper::AVAILABLE_OPTIONS, true),
31+
\ARRAY_FILTER_USE_KEY
32+
);
2433

25-
return null;
26-
}
34+
$trace = $options['_trace'] ?? null;
35+
unset($options['_trace']);
2736

2837
if (array_key_exists(0, $vars) && 1 === count($vars)) {
29-
VarDumper::dump($vars[0]);
38+
VarDumper::dump($vars[0], null, $options);
3039
$k = 0;
3140
} else {
41+
$vars = array_filter($vars, static fn (int|string $key) => !str_starts_with($key, '_'), \ARRAY_FILTER_USE_KEY);
42+
43+
if (!$vars) {
44+
VarDumper::dump(new ScalarStub('🐛'), null, $options);
45+
46+
return null;
47+
}
48+
3249
foreach ($vars as $k => $v) {
33-
VarDumper::dump($v, is_int($k) ? 1 + $k : $k);
50+
if (array_key_last($vars) === $k) {
51+
$options['_trace'] = $trace;
52+
}
53+
54+
VarDumper::dump($v, is_int($k) ? 1 + $k : $k, $options);
3455
}
3556
}
3657

@@ -45,7 +66,7 @@ function dump(mixed ...$vars): mixed
4566
if (!function_exists('dd')) {
4667
function dd(mixed ...$vars): never
4768
{
48-
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) {
69+
if (!in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) {
4970
header('HTTP/1.1 500 Internal Server Error');
5071
}
5172

@@ -55,11 +76,21 @@ function dd(mixed ...$vars): never
5576
exit(1);
5677
}
5778

79+
$options = array_filter(
80+
$vars,
81+
static fn (mixed $key): bool => in_array($key, VarDumper::AVAILABLE_OPTIONS, true),
82+
\ARRAY_FILTER_USE_KEY
83+
);
84+
5885
if (array_key_exists(0, $vars) && 1 === count($vars)) {
59-
VarDumper::dump($vars[0]);
86+
VarDumper::dump($vars[0], null, $options);
6087
} else {
6188
foreach ($vars as $k => $v) {
62-
VarDumper::dump($v, is_int($k) ? 1 + $k : $k);
89+
if (str_starts_with($k, '_')) {
90+
continue;
91+
}
92+
93+
VarDumper::dump($v, is_int($k) ? 1 + $k : $k, $options);
6394
}
6495
}
6596

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