A Better dd() for Your TDD

An important part of every Laravel developer's debugging arsenal is the humble dd() helper function—"dump and die"—to output the contents of a variable and terminate execution of your code. In the browser, dd() results in a structured, easy-to-read tree, complete with little arrow buttons that can be clicked to expand or hide children of nested structures. In the terminal, however, it's a different story.

When doing a lot of testing, we end up spending a quite a bit of time inspecting output in the terminal, rather than in the browser. Often, while troubleshooting a failing test in PHPUnit, you'll find the need to dd() the value of an array or collection, the contents of some class or an Eloquent model, or to inspect the JSON response from an API call. The command line output of dd() is structured the same as the HTML—but many times, the output zooms by and is either very long or deeply nested, leading to a whole lot of scrolling to hunt down the information you were looking for. Finding the top of the output can be difficult, or in some cases impossible, if the output length exceeds your terminal's buffer.

Fortunately, it's simple to build your very own customized version of dd() to help tame your unwieldly terminal output—helping you find the details you're interested in quickly, without wearing out your trackpad (and your patience). With just one helper function, you can go from this: so much scrolling

...to this: dd tamed

...or anything in between.

There are two ways to go about this:

Option 1: Using Symfony's VarDumper

Since version 5, Laravel's dd() (and it's cousin, dump(), which doesn't halt execution) has relied on Symfony's VarDumper component under the hood. So the easiest way to supercharge your dd() is to write a helper function that gives you access to some of the additional configuration methods in VarDumper.

An easy place for this helper function to live is within a helpers.php file in your app directory, which you can autoload by adding it to composer.json:

"autoload": {
"files": [
"app/helpers.php"
],
},
...

You can store other helper functions here as well, modeled after Laravel's /Illuminate/Foundation/helpers.php file. If you're only adding functions related to development, the file can be autoloaded under the autoload-dev key. In addition, if you wish to actually override Laravel's dd() function (or any other Laravel helper function, for that matter) rather than using a different name for your customized function, you would need to require your helper file in /bootstrap/autoload.php before the line require __DIR__.'/../vendor/autoload.php'.

In your new app/helpers.php file, we'll create a new function called ddd(), which will accept the variable we wish to dump, along with whatever other parameters we want to use to customize our output. As a starting point, let's start with a bare-bones function that just mimics the behavior of the built-in dd():

<?php
 
use Illuminate\Support\Debug\HtmlDumper;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
 
function ddd($variable)
{
$cloner = new VarCloner();
 
$dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper();
$dumper->dump($cloner->cloneVar($variable));
 
die(1);
}

VarDumper works by first cloning the input variable with a "Cloner", then dumping it to either the browser or the terminal with with a "Dumper". The VarCloner class, and the Data object that it creates, both have some very handy configuration options that we can now take advantage of. On the cloner, setMaxString() will truncate long strings to the specified length. setMaxItems() will limit the number of items that get displayed past the first nesting level; for example, setMaxItems(6) will turn this output:

array:2 [
0 => array:5 [
"id" => 1
"foo" => "FooValue1"
"bar" => "BarValue1"
"baz" => "BazValue1"
"qux" => array:2 [
"average" => 3.0
"standard_deviation" => 1.0
]
]
0 => array:5 [
"id" => 1
"foo" => "FooValue2"
"bar" => "BarValue2"
"baz" => "BazValue2"
"qux" => array:2 [
"average" => 5.0
"standard_deviation" => 1.0
]
]
...

into this:

array:2 [
0 => array:5 [
"id" => 1
"foo" => "FooValue1"
"bar" => "BarValue1"
"baz" => "BazValue1"
"qux" => array:2 [ …2]
]
1 => array:5 [
"id" => 2
…4
]
...

...which, in the case of very long output, is a great way to get a quick look at just the top few items. (Note that passing a value of -1 to any of these configuration methods is the equivalent of having no limit on the output.)

The results of VarCloner can be further modified before they are dumped. The method withMaxItemsPerDepth() will limit the number of items that are displayed at each level of depth, while withMaxDepth()—which I've found to be most useful for whipping your output into shape—will limit the number of nested child levels that are displayed in their expanded form. In the example above, a setting of withMaxDepth(2) would collapse all the qux arrays, giving us:

array:2 [
0 => array:5 [
"id" => 1
"foo" => "FooValue1"
"bar" => "BarValue1"
"baz" => "BazValue1"
"qux" => array:2 [ …2]
]
1 => array:5 [
"id" => 1
"foo" => "FooValue2"
"bar" => "BarValue2"
"baz" => "BazValue2"
"qux" => array:2 [ …2]
]
...

Going back to our helper function, we can accept some parameters that will give us on-the-fly control of how our ddd() output is displayed:

function ddd($variable, $depth = -1, $stringLength = 20)
{
$cloner = new VarCloner();
$cloner->setMaxString($stringLength);
 
$dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper();
$dumper->dump($cloner->cloneVar($variable)->withMaxDepth($depth));
 
die(1);
}

So now, if we call ddd($someVariable, 2) our output will be limited to two levels deep, with long strings truncated to 20 characters. If we don't get enough detail to find what we're looking for, we can very simply drop down a level with ddd($someVariable, 3). It's a quick and easy way to whip your debugging output into shape, particularly when inspecting deeply-nested JSON objects or a long Eloquent model.

Option 2: Kint

An alternative is to use the debugging library Kint to accomplish the same thing, but with slightly different results. Kint offers some really handy enhancements out-of-the-box for controlling debugging output in your browser, but like VarDumper, it gives access to two configuration options for terminal output that are useful: $maxLevels and $maxStrLength.

First, install Kint using composer:

composer require raveren/kint

Then, in the app/helpers.php file we created earlier, we can make a custom function called debug:

function debug($variable, $depth = null)
{
\Kint::$maxLevels = $depth;
 
return ddd($variable);
}

Note: Kint includes its own functions named ddd() and dump(), so we need to use a new name; our debug() function calls Kint's built-in ddd() function.

And just like that, we can call debug($someVariable, 3) to cut our output down to size again. Like VarDumper, Kint's output is nicely formatted and color-coded; one advantage to using Kint in place of VarDumper is that Kint's output includes more details about data types in its results, making it very easy to spot when some value that should be an integer is actually a float, for instance.


Do you have your own great tips for tricking out your testing setup? Share them with us on Twitter at @TightenCo.

Get our latest insights in your inbox:

By submitting this form, you acknowledge our Privacy Notice.

Hey, let’s talk.
©2024 Tighten Co.
· Privacy Policy