digitalnature/php-ref
php-ref is a drop-in replacement for print_r and var_dump that renders PHP variables in a clean, interactive, syntax-highlighted interface with collapsible structures, search, and keyboard navigation—making debugging complex arrays and objects faster and easier.
Install it as a dev dependency (it ships as a single file plus a tiny loader):
composer require --dev digitalnature/php-ref
If the package's autoload doesn't register the global function for you, or you're dropping it into a legacy project, just include the file directly:
require_once 'vendor/digitalnature/php-ref/ref.php'; // or a copied ref.php
Then replace a var_dump with a single call:
$user = [
'id' => 42,
'name' => 'Ada',
'roles' => ['admin', 'developer'],
];
ref($user);
You get a collapsible, syntax-highlighted tree with types, string lengths, array counts, and object class names. In a browser it renders HTML; from the CLI it falls back to plain text automatically.
Where to look first: open ref.php and read the defaults array inside the ref class. Everything you can tune is exposed through the static ref::config($key, $value) method — that's the only API surface you'll normally touch. The ref() function itself is a thin wrapper around the dumper, so if you need to understand output behavior, read the class, not the function.
First real use case: debugging a framework object without drowning. In a Laravel controller:
ref($order->load('items.product')); // collapsible tree, not a 4000-line var_dump
Gate it to local/dev environments. The function echoes directly, so an accidental call in production corrupts responses. Either wrap it:
function d($value): void
{
if (app()->environment('local')) {
ref($value);
}
}
…or at minimum make it a habit to grep -rn "ref(" before deploying.
Dumping several values at once. The dumper takes one value; wrap multiple variables in an array to get them in one tree:
ref(['request' => $request->all(), 'user' => $user, 'session' => session()->all()]);
Turn on call-site context. When you have five ref() calls in a request, you need to know which one fired. The config exposes a debug mode that annotates output with file and line — check the defaults in ref.php for the exact key name in your version and set it once in a bootstrap file:
ref::config('debug_mode', true);
Use text mode for CLI, logs, and API responses. HTML output inside a JSON endpoint turns a valid payload into garbage. Before dumping in a non-HTML context, switch the render mode or capture the output:
ob_start();
ref($payload);
$dump = ob_get_clean();
file_put_contents(storage_path('logs/dump.txt'), $dump);
Tune depth for framework objects. Eloquent models, service containers, and DI graphs have enormous reachable object trees. Cap traversal so a dump stays readable:
ref::config('max_depth', 4);
Raise it temporarily when you actually need to inspect a deep relation, then lower it again.
Custom theme for large dumps. The library builds its own styles; if you want it to match your app or read better on a dark screen, the config accepts CSS overrides (see the CSS-related keys in the defaults array). Set them in your local bootstrap rather than editing ref.php, so upgrades don't clobber your changes.
Laravel integration. Add the require_once to AppServiceProvider::register() guarded by $this->app->environment('local'). That way the function exists everywhere in local without polluting production autoloading.
Content-Type: application/json output produces an invalid response that your front end will report as a mysterious parse error. Use text mode or capture.header() call or redirect after a debug dump in the same request will fail. Move the call or wrap in ob_start().style-src policies block the inline styles, leaving an unstyled wall of text. Loosen the policy in local, or accept the plain output.max_depth and dumping toArray() instead of the live object are your first defenses.ref() is a plain global function; if another package defines it you get a fatal redeclare. Check before installing alongside other dumpers. You can always wrap it in your own namespaced helper.composer.json and don't let it into production dependencies — install with --dev.max_depth, not a rendering bug. If nothing appears at all, check that the function was actually loaded — a missing require/autoload is the most common cause of a silent no-op (or a fatal "undefined function" if PHP's error reporting is suppressed).ref( outside allowed files; it's cheaper than finding one in a customer-facing response.How can I help you explore Laravel packages today?