Skip to content

[VarDumper] Allow dd() to be called without arguments #28317

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

Merged
merged 1 commit into from
Sep 21, 2018
Merged

[VarDumper] Allow dd() to be called without arguments #28317

merged 1 commit into from
Sep 21, 2018

Conversation

SjorsO
Copy link
Contributor

@SjorsO SjorsO commented Aug 30, 2018

Q A
Branch? master
Bug fix? no
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

Description
A while back the dd() helper was added to the VarDumper component which was (i think) inspired by Laravel's dd() helper. Laravel has removed their version of the helper in favor of the helper in Symfony.

However, as opposed to the Laravel helper, the Symfony helper requires at least one argument. Calling the Laravel helper with no arguments simply killed the program (and usually showed a white screen), calling the Symfony helper with no arguments throws a TypeError: Too few arguments to function dd() exception (which gets rendered by the error handler and fills the whole screen with useless information).

Being able to call the dd() helper with no arguments is useful because it is a quick way to tell you if your code reaches a certain point. If it does, you can fill in the dd() with variables to keep debugging.

This PR allows the dd helper to be called without arguments.

This PR also makes the helper call die instead of exit to better reflect the function name 😄

@ro0NL
Copy link
Contributor

ro0NL commented Aug 30, 2018

i like the required var as a function semantic =/ Not sure dd() and die() being equivalent makes sense

@mvannes
Copy link

mvannes commented Aug 30, 2018

But isn't this just calling die();? The name even implies that you want to dump something. If you don't want to dump something, why would you want to call something named dump die?

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Aug 30, 2018

Thanks for submitting, that's indeed a difference with the previous Laravel's dd() function.
But I agree with previous comments, you should use die() instead in this case.
👎 on my side.

@nicolas-grekas nicolas-grekas added this to the next milestone Aug 30, 2018
@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 30, 2018

With Laravel's helper you could move around an empty dd() to figure out if your code reached a certain point. There was no reason to use die or exit because dd() did the same thing, with the added benefit of easily being able to put variables in there for it to dump. I don't see a good reason for Symfony's helper to have changed this behavior.

Also, allowing it to be called with no arguments has the (very) minor advantage that programs alway die when they call dd(), even when you forget to give it an argument. Currently, if you have a try/catch around your code that catches either Errors or Throwables, and you by mistake call dd() without arguments, the program won't die.

Apart from making the function less semantic (which i'd argue doesn't make a difference in this case), is there a good reason to not allow dd() to be called without arguments?

@ro0NL
Copy link
Contributor

ro0NL commented Aug 30, 2018

Currently, if you have a try/catch around your code that catches either Errors or Throwables, and you by mistake call dd() without arguments, the program won't die.

Hm that's a strong argument actually :)

@curry684
Copy link
Contributor

curry684 commented Aug 30, 2018

Apart from that the generic code now after the second commit is also cleaner.

nicolas-grekas
nicolas-grekas previously approved these changes Aug 31, 2018
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

ok, looks like you have some supporters :)

@nicolas-grekas
Copy link
Member

What about making this a bit more useful and make it dump the stack trace instead of nothing?

Here is a patch doing so:
image

diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php
index f3af638d94..35f2f2b63a 100644
--- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php
@@ -111,7 +111,7 @@ class ExceptionCaster
 
     public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $isNested)
     {
-        if (!$isNested) {
+        if (!$isNested && !$trace->castWhenRoot) {
             return $a;
         }
         $stub->class = '';
diff --git a/src/Symfony/Component/VarDumper/Caster/TraceStub.php b/src/Symfony/Component/VarDumper/Caster/TraceStub.php
index 5eea1c8766..c3c51bb1ef 100644
--- a/src/Symfony/Component/VarDumper/Caster/TraceStub.php
+++ b/src/Symfony/Component/VarDumper/Caster/TraceStub.php
@@ -24,13 +24,15 @@ class TraceStub extends Stub
     public $sliceOffset;
     public $sliceLength;
     public $numberingOffset;
+    public $castWhenRoot;
 
-    public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0)
+    public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0, bool $castWhenRoot = false)
     {
         $this->value = $trace;
         $this->keepArgs = $keepArgs;
         $this->sliceOffset = $sliceOffset;
         $this->sliceLength = $sliceLength;
         $this->numberingOffset = $numberingOffset;
+        $this->castWhenRoot = $castWhenRoot;
     }
 }
diff --git a/src/Symfony/Component/VarDumper/Resources/functions/dump.php b/src/Symfony/Component/VarDumper/Resources/functions/dump.php
index 1ea3dc8434..46dbb3a434 100644
--- a/src/Symfony/Component/VarDumper/Resources/functions/dump.php
+++ b/src/Symfony/Component/VarDumper/Resources/functions/dump.php
@@ -10,6 +10,7 @@
  */
 
 use Symfony\Component\VarDumper\VarDumper;
+use Symfony\Component\VarDumper\Caster\TraceStub;
 
 if (!function_exists('dump')) {
     /**
@@ -32,11 +33,13 @@ if (!function_exists('dump')) {
 }
 
 if (!function_exists('dd')) {
-    function dd($var, ...$moreVars)
+    function dd(...$vars)
     {
-        VarDumper::dump($var);
+        if (!$vars) {
+            VarDumper::dump(new TraceStub(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), false, 0, null, 0, true));
+        }
 
-        foreach ($moreVars as $var) {
+        foreach ($vars as $var) {
             VarDumper::dump($var);
         }
 

@nicolas-grekas
Copy link
Member

Could also be done for dump() btw if you like the idea.

@nicolas-grekas nicolas-grekas changed the title Allow dd() to be called without arguments [VarDumper] Allow dd() to be called without arguments Aug 31, 2018
@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 31, 2018

I always liked the way the Laravel helper worked. If i put an empty dd() in my code and then run my unit tests, it would simply stop execution, leaving my console clean. Stacktraces can get pretty long, especially in feature tests that hit controllers. For example, dumping a stacktrace in one of my controllers when running feature tests prints a list of 74 calls (most of which are middleware or Routing/Pipeline.php)

I can imagine situations where it is useful to dump a stacktrace, but i'd prefer that to be a separate helper, or maybe a static method on the VarDumper class.

@ro0NL
Copy link
Contributor

ro0NL commented Aug 31, 2018

What about something minimal, just a file/line ref of where dd occurred.

@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 31, 2018

I like that idea. Dumping the last call (or maybe last 3 calls) of the stacktrace would be useful, and it would be clean enough to not flood the console.

@nicolas-grekas
Copy link
Member

PR update welcome then (the number of frames can be limited thanks to debug_backtrace()'s 2nd argument)

@curry684
Copy link
Contributor

curry684 commented Aug 31, 2018

What about something minimal, just a file/line ref of where dd occurred.

I think that's generically useful, independent of whether parameters were supplied to always show it when invoked.

Execution halted by call to dd(...) at /my/project/folder/file.php line 43.

Then followed by the sequential dumps of the parameters, if supplied.

I wouldn't include more than 1 line of callstack as indeed it's not up to us whether to judge by default how many lines are relevant. 2 or 3 would also be arbitrarily either too much or too few for any specific situation.

@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 31, 2018

I've changed the PR to dump a reference of where the dd() occurred.

It looks like this in the console:

image

and like this in the browser:

image

@curry684
Copy link
Contributor

curry684 commented Aug 31, 2018

Looks good to me presentation-wise.

Anyone care to chime in on my opinion that we should standardize this to always dump the call location and not "magically" change behavior based on parameters or not?

@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 31, 2018

I think that for debug-only helpers like this being useful is most important, we shouldn't worry about semantics or "magic".

@curry684
Copy link
Contributor

curry684 commented Aug 31, 2018

Why is showing the location of the break suddenly not useful anymore when also dumping parameters?

"Magic" is not bad per se, we do a lot of awesome magic in Symfony like autowiring services. "Magic" is cool when it boils down to "it just works". "Magic" becomes bad when it's not consistent.

@SjorsO
Copy link
Contributor Author

SjorsO commented Aug 31, 2018

Oh, my bad. I thought you meant removing the ability to dump vars, i thought it was strange 😄

I'd prefer not changing the helper's behavior when it's given arguments, it has worked like this for a long time in Laravel, as far as i know everyone's happy with it.

@fancyweb
Copy link
Contributor

+1 to curry684 suggestion to add this behavior to every dump() call.
Sometimes it's hard to find the dump() you've put in vendor files etc..

@SjorsO
Copy link
Contributor Author

SjorsO commented Sep 3, 2018

I'd prefer to keep this PR like it is now. It only restores something i used to be able to do in Laravel, and doesn't change the behavior of other helpers.

We can always open another PR to change the behavior of dump()

@curry684
Copy link
Contributor

curry684 commented Sep 3, 2018

I didn't suggest changing dump actually, that's indeed a different discussion. I'm just saying I severely dislike that right now the behavior between non-parametrized and parametrized invocation is inconsistent.

Imho it should just be:

if (!function_exists('dd')) {
    function dd(...$vars)
    {
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
        VarDumper::dump(new TraceStub($backtrace, false, 0, null, 0, true));
        foreach ($vars as $var) {
            VarDumper::dump($var);
        }
        die(1);
    }
}

Either do it never, or always. In dd.

@SjorsO
Copy link
Contributor Author

SjorsO commented Sep 3, 2018

To keep the helper consistent, i've changed the PR to never make it print the backtrace. It now works exactly like the Laravel helper used to.

@nicolas-grekas nicolas-grekas dismissed their stale review September 3, 2018 14:47

too many comments since then

@curry684
Copy link
Contributor

curry684 commented Sep 3, 2018

That's fine with me, we can then have the discussion about changing the behavior elsewhere as it seems to have more implications than initially anticipated.

(kicking off the other discussion: I'd also suggest actually making VarDumper a tad more configurable, ie. have some methods VarDumper::setTraceDepth(3); that one can override from sane universal defaults in their own front controller or in config/packages/dev/debug.yaml, as yes it does make sense to also dump the call stack sometimes in dump(...), but that could even have consequences in the web profiler toolbar as it needs to be rendered there etc.)

@javiereguiluz
Copy link
Member

I don't understand why are we trying to complicate things so much here 😕

dd() means: dump() what I pass you ... and then die() immediately.

Well, if I use dd() without arguments, what I expect is:

  • dump() what I pass you: I haven't passed anything, so don't dump anything
  • die() immediately

So doing the change proposed originally in this PR is the most consistent behavior in my opinion. There's no need to add stack traces or any other information.

@curry684
Copy link
Contributor

curry684 commented Sep 6, 2018

the change proposed originally in this PR

See the commits, it's been brought back to that state 😉

@javiereguiluz
Copy link
Member

Great!

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

(I'll propose displaying file+line info in a separate PR)

@javiereguiluz
Copy link
Member

@SjorsO could you please fix the merge conflict to make this PR mergeable again? Thanks!

@SjorsO
Copy link
Contributor Author

SjorsO commented Sep 21, 2018

@javiereguiluz done

@nicolas-grekas
Copy link
Member

Thank you @SjorsO.

@nicolas-grekas nicolas-grekas merged commit a73dfad into symfony:master Sep 21, 2018
nicolas-grekas added a commit that referenced this pull request Sep 21, 2018
…(SjorsO)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[VarDumper] Allow dd() to be called without arguments

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

**Description**
A while back the `dd()` helper was [added to the VarDumper component](#26970) which was (i think) inspired by Laravel's `dd()` helper. Laravel has [removed their version of the helper](laravel/framework#25087) in favor of the helper in Symfony.

However, as opposed to the Laravel helper, the Symfony helper requires at least one argument. Calling the Laravel helper with no arguments simply killed the program (and usually showed a white screen), calling the Symfony helper with no arguments throws a `TypeError: Too few arguments to function dd()` exception (which gets rendered by the error handler and fills the whole screen with useless information).

Being able to call the `dd()` helper with no arguments is useful because it is a quick way to tell you if your code reaches a certain point. If it does, you can fill in the `dd()` with variables to keep debugging.

This PR allows the dd helper to be called without arguments.

This PR also makes the helper call `die` instead of `exit` to better reflect the function name 😄

Commits
-------

a73dfad [VarDumper] Allow dd() to be called without arguments
@nicolas-grekas nicolas-grekas modified the milestones: next, 4.2 Nov 1, 2018
This was referenced Nov 3, 2018
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.

9 participants