Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VarDumper] Add an options builder to configure VarDumper's behavior at runtime #48667

Open
wants to merge 1 commit into
base: 6.3
Choose a base branch
from

Conversation

alexandre-daubois
Copy link
Contributor

@alexandre-daubois alexandre-daubois commented Dec 15, 2022

Q A
Branch? 6.3
Bug fix? no
New feature? yes
Deprecations? no
Tickets Fix #48474
License MIT
Doc PR Todo

Following up #48432, this PR brings an options builder to configure VarDumper's behavior thanks to a special _options named argument. Here is a snippet of it in action and a screenshot of the result. Feedbacks on UI are really welcomed, especially about the debug backtrace!

#[AsController]
#[Route('/')]
class FooController
{
    public function __invoke(): Response
    {
        $options = (new VarDumperOptions())
            ->trace()
            ->stringLength()
        ;

        dump(var: ['Hi', 'Greetings' => 'Good morning, Symfony!'], _options: $options);
        dump('Classic dump');
        dd(['Options are cloned!'], named: 'This is named', _options: (clone $options)->lightArray()->traceLimit(2));

        throw new \LogicException('This code should never be reached!');
    }
}

Capture d’écran 2022-12-12 à 21 38 20

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Thanks for tackling this, that's a complex feature because the matrix of possibilities is huge.

Here are some random thoughts:

  • I like the style proposed in the issue: dump()->trace()->maxDepth(4)->values($var);. I'm just wondering if values() shouldn't be named dump() or var() or ?
  • I also like the dump($var, _trace: true) style, which could work well with autocompletion if we describe the options as virtual arguments on the function? array{_trace: bool, ...}
  • instead of trace_limit, what about trace: bool|int?
  • have a look at FrameStub/TraceStub for dumping traces
  • we might want to provide a way to define the default options on the VarDumper class?

@lyrixx
Copy link
Member

lyrixx commented Dec 16, 2022

  • I like the style proposed in the issue: dump()->trace()->maxDepth(4)->values($var);. I'm just wondering if values() shouldn't be named dump() or var() or ?

As states by @derrabus, this cannot work


I don't really like the current object API:

$options = (new VarDumperOptions())
    ->trace()
    ->stringLength()
;

it's too long to type. We should not forgot we are a in debug context. So we want to be able to get a result ASAP. that's why we have the function dd(). As short as possible.

That's why I prefer the second bullet point of @nicolas-grekas comment:

dd($this, _trace: true, _maxDepth: 4);

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Dec 16, 2022

#48474 (comment), this cannot work

yes it can, see #48474 (comment) ;)

@lyrixx
Copy link
Member

lyrixx commented Dec 16, 2022

This is ugly :) I really don't like it. And it's too slow to type.

@ro0NL
Copy link
Contributor

ro0NL commented Dec 16, 2022

how is a fully autodiscoverable dump()->... too slow to type?

@lyrixx
Copy link
Member

lyrixx commented Dec 16, 2022

@ro0NL I mixed my thought :/ I was talking about:

$options = (new VarDumperOptions())
    ->trace()
    ->stringLength()
;

dump($this, _options: $options);

@ro0NL
Copy link
Contributor

ro0NL commented Dec 16, 2022

I'm just wondering if values() shouldn't be named dump() or var() or ?

maybe __invoke works, IIUC we could do

dump(1,2,3);
dump()(1,2,3)->trace();
dump()->trace()(1,2,3);

@alexandre-daubois
Copy link
Contributor Author

alexandre-daubois commented Dec 16, 2022

I agree the current object API is a bit much to type. I am 👍 to find a better solution.

Should I try to implement both @GromNaN solution (dump returning mixed|VarDumperOptions) and @nicolas-grekas' one, together? I wonder if it would be a bit disturbing to have both ways available, and I don't have, yet, a strong opinion for one solution over the other.

Also, I really like the solution proposed by @ro0NL. If I may find a drawback, I'm afraid this syntax could be difficult to be "discovered" by yourself without reading the documentation.

@GromNaN
Copy link
Member

GromNaN commented Dec 16, 2022

The syntax that don't work is the one that rely on __destruct.
The last suggestion mentioned by @nicolas-grekas is dump()->trace()->maxDepth(4)->dump($var) which returns the $var as expected.

@alexandre-daubois
Copy link
Contributor Author

alexandre-daubois commented Dec 17, 2022

I updated the feature. The new version allows you to do both ways:

// Passing options as named parameters
dump('Values', _trace: 5, _flags: AbstractDumper::DUMP_STRING_LENGTH);

// Use the options builder syntax
dump()
    ->trace(5)
    ->stringLength()
    ->dd('Values') // or ->dump('Values')
;

Still investigating on how to use TraceStub. Otherwise, this works well! And it is so much cleaner and quicker to type.

Also, I made the choice to only have the options builder syntax on dump, not dd. I did this, because I think it is, in my opinion, a good thing to keep never return type on dd (because never|VarDumpOptions is obviously a forbidden return type). Of course, passing options as named parameters to dd also works!

/**
* @param string|null $label
* @param VarDumperOptions|iterable $options
Copy link
Contributor

@ro0NL ro0NL Dec 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/iterable/array/

@alexandre-daubois
Copy link
Contributor Author

alexandre-daubois commented Dec 19, 2022

I've been able to use TraceStub to display the backtrace. Here is the result:

image

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

About the default options, I'd suggest configuring them via a VAR_DUMPER_OPTIONS env var. I'd go with a url-encoded string so that it's easy to turn into an options array

},
"require-dev": {
"symfony/config": "^5.4|^6.0",
"symfony/web-profiler-bundle": "^5.4|^6.0"
},
"conflict": {
"symfony/config": "<5.4",
"symfony/dependency-injection": "<5.4"
"symfony/dependency-injection": "<5.4",
"symfony/var-dumper": "<6.3"
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

@@ -6,6 +6,7 @@ CHANGELOG

* Add caster for `WeakMap`
* Add support of named arguments to `dd()` and `dump()` to display the argument name
* Add support of dumper's behavior configuration with the special `_options` named argument for `dump` and `dd`
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep thins simple, eg
Allowing configuring options when dumping

/**
* @author Alexandre Daubois <alex.daubois@gmail.com>
*/
enum HtmlDumperTheme: string
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be removed as it closes the list of possible themes, while the list is open for extensibility

$this->options = array_replace($this->options, \array_intersect_key($options, $this->options));
}

public function dump(mixed ...$vars): mixed
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would also benefit from the generics annotation

$vars = [new ScalarStub('🐛')];
}

return dump(...($vars + $this->options));
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for the brackets

using dump() instead of VarDumper::dump() is adding one extra frame in the backtrace.
it might be better to skip this frame (same for dd() below

return $this->options;
}

public function set(string $option, mixed $value): static
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed I suppose

*
* @internal
*/
final class VarClonerFactory
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd better not add this class, it adds indirection

}

/**
* Set flags manually. Valid flags are {@see AbstractDumper::DUMP_*} constants.
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param AbstractDumper::DUMP_* $flags

if (!$vars) {
VarDumper::dump(new ScalarStub('🐛'));
$options = new VarDumperOptions($vars);
$vars = \array_diff_key($vars, $options->getOptions());
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather ignore any arg that tarts with a _ in the foreach below

}

if (self::requiresRegister($options)) {
self::register($options);
Copy link
Member

@nicolas-grekas nicolas-grekas Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will change the options of the default handler when any options changes?
this would be unexpected to me.

@dunglas
Copy link
Member

dunglas commented Dec 22, 2022

When dumping large objects, it could be nice to be able to have the possibility to dump only one or several properties instead of the full object. Something like: _path: 'foo.bar' and _path: ['foo.bar', 'baz.bat'] maybe?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add options to dump function
7 participants