Dumpio

Dokumentace

dumpio/client is the official PHP package for sending dumps to the Dumpio app. It's the richest way to use Dumpio from PHP: instead of hand-writing JSON, you call a helper like dio($user) and the package builds the whole message for you — types, colors, labels, the file and line it was called from, even HTML and email previews.

This page is written to get you productive even if you've never used a tool like this. If you use Laravel or Symfony, read this page first, then see the dedicated PHP + Laravel and PHP + Symfony pages for the framework parts.

What is a "dump"?

A dump is a snapshot of a value in your running program, sent to the Dumpio window so you can look at it. It's the same idea as PHP's var_dump() or Laravel's dd() — except the data travels over the network to a separate app instead of being printed into your page or API response.

Why that's better for debugging:

  • It doesn't break your output. You can inspect a variable in the middle of a JSON API, a queued job, or a Livewire component without corrupting the response.
  • It keeps the types. You see that 42 is an int and "paid" is a string, and for objects you get the class name and which properties are public/protected/private.
  • It never crashes your app. Every call is wrapped in a try/catch. If the Dumpio app isn't running, the dump is quietly dropped — your code runs exactly as before.

Before you start

  1. The Dumpio app is running with its default server (HTTP on 127.0.0.1:21234). See Installation.
  2. PHP 8.0 or newer.

Install

1composer require dumpio/client

That's it — no configuration needed while the Dumpio app is on your machine using the default port.

Tip: install it as a dev dependency (composer require --dev dumpio/client) if you only debug locally. The package is a safe no-op in production either way (see Production safety).

Your first dump

Put this anywhere in your code — a controller, a script, a test — and load the page or run the script:

1use Dumpio\Dumpio;
2 
3$user = ['id' => 7, 'name' => 'Ada', 'roles' => ['admin', 'billing']];
4 
5Dumpio::dump($user);

Switch to the Dumpio window and you'll see a new entry at the top. Click it and the value is shown as an expandable, typed tree:

Nothing showing up? Jump to Troubleshooting at the bottom of this page.

The four ways to send a value

There are four entry points. They do the same core thing — send a value — but differ in what they return and whether they stop your script. Pick whichever fits the line you're editing.

1use Dumpio\Dumpio;
2 
3// 1) The plain call
4Dumpio::dump($user);
5 
6// 2) dio() — the fluent builder (recommended). Lets you chain color/label/etc.
7dio($user)->green()->label('current user');
8 
9// 3) dumpio() — "tap". Sends the value AND returns it unchanged.
10$total = dumpio($cart->total(), 'total'); // $total still holds the number
11 
12// 4) ddio() — "dump and die". Sends every argument, then stops the script.
13ddio($request->all(), $user);
I want to… Use
Quickly show a value Dumpio::dump($x) or dio($x)
Add a color / label / channel dio($x)->red()->label('…')->channel('…')
Inspect a value without breaking a chain or expression dumpio($x) (it returns $x)
Stop the script right here and look ddio($x)
Send something structured (an exception, a query, an email…) the typed helpers below

dio(), dumpio(), and ddio() are plain global functions — they're autoloaded by the package, so you can call them from anywhere without a use statement.

The "tap" trick

Because dumpio() returns its argument, you can wrap any expression with it and see the value without changing what your code does:

1return dumpio($service->build($request), 'built'); // dumps, then returns the value

Colors, labels, and channels

These three things help you find the dump you care about in a busy list.

  • Flag (color): one of red, yellow, blue, green, purple, pink, gray. Use it however you like — e.g. red for problems, green for success. In the viewer you can filter by color.
  • Label: the title shown in the list. Without one, the list shows a generic title.
  • Channel: a group name shown as #name. Handy for separating, say, #checkout dumps from #auth dumps. You can filter by channel too.
1dio($order)
2 ->green() // color
3 ->label('new order') // title in the list
4 ->channel('checkout'); // group

The fluent builder

dio() (and Dumpio::make()) return a builder you can chain on. The dump is sent automatically when the builder is done — either when you call ->send() or when it goes out of scope — so ->send() is optional.

1dio($cart)
2 ->purple() // or ->flag('purple')
3 ->label('cart')
4 ->channel('checkout')
5 ->send(); // optional
  • Colors: red(), yellow(), blue(), gray(), purple(), pink(), green() (or flag('…')).
  • Text: label('…'), channel('…').
  • Conditions: when($bool) sends only if true, unless($bool) is the opposite.
1dio($internalState)->when($debugMode); // sent only when $debugMode is true

Debugging inside loops (flood control)

A dump inside a loop can send hundreds of entries and drown the viewer. These modifiers keep that under control. They're keyed to the exact line in your code (per process), so each call site counts on its own:

1foreach ($rows as $row) {
2 dio($row)->once(); // send only the FIRST iteration
3 dio($row)->limit(5); // send at most 5
4 dio($row)->count(); // send ONE entry that shows "×N" and counts up
5}

count() is the friendliest for loops: instead of N rows you get a single live-updating entry with a ×N badge. Pass a name to share one counter across several places: dio($x)->count('processed rows').

Timing a block

A stopwatch measures how long something takes and sends it as a measure dump (with elapsed milliseconds and the memory change):

1$sw = Dumpio::stopwatch('import');
2// … first part …
3$sw->lap('parsed'); // a split time; the stopwatch keeps running
4// … second part …
5$sw->stop('done'); // the final time

Interactive breakpoint

Dumpio::pause() stops execution and blocks the request until you click Continue or Stop in the viewer — a breakpoint without a debugger.

1Dumpio::pause('before charging the card');

Stop ends the script (exit(1)); Continue lets it proceed. It's a safe no-op when Dumpio is disabled or the app isn't running, works on loopback servers only (a network-exposed server continues immediately), and a viewer-side timeout auto-continues so a forgotten breakpoint never hangs forever.

Update in place

Dumpio::update($id, $value) sends a keyed dump: send again with the same $id and the viewer replaces the existing entry instead of adding a row — perfect for a live counter or a value that changes across a loop. Dumpio::remove($id) drops it.

1foreach ($rows as $i => $row) {
2 Dumpio::update('import', ['done' => $i + 1, 'total' => count($rows)]);
3 // …
4}
5Dumpio::remove('import');

Typed helpers

So far we've dumped plain values. Dumpio also has typed helpers for common debugging targets — exceptions, SQL queries, HTTP calls, and more. Each one gets its own nicely formatted view in the app (see Dump types) and picks a sensible default color. They're all static methods on Dumpio and, like everything else, never throw.

Method What it's for Default color
Dumpio::exception(\Throwable $e, $context = [], $opts = []) An exception, with a parsed stack trace red
Dumpio::query($sql, $bindings = [], $timeMs = null, $opts = []) A database query (with its parameters and time) purple
Dumpio::http($method, $url, $status = null, $opts = []) An HTTP request/response (headers, body, responseTime) by status
Dumpio::log($level, $message, $details = [], $opts = []) A log line by level
Dumpio::model($class, $attributes, $opts = []) One ORM record (relations, exists, connection)
Dumpio::collection($items, $opts = []) A list of items (message)
Dumpio::table($columns, $rows, $opts = []) An explicit table
Dumpio::measure($name, $timeMs, $opts = []) One timing (memory, context)
Dumpio::performance($metrics, $opts = []) A bundle of metrics (breakdown, context)
Dumpio::event($event, $opts = []) A business/domain event (entity, actor, data, …)
Dumpio::trace(?$label = null, $opts = []) The current call stack ("how did we get here?") gray
Dumpio::memory(?$label = null, $opts = []) A memory snapshot (current/peak/limit) blue
Dumpio::html($html, $opts = []) Preview an HTML fragment in a safe sandbox
Dumpio::mail($mail, $opts = []) Preview an email (html, text, subject, to, …)
Dumpio::mailable($mailable, $opts = []) Render and preview a Laravel Mailable

Automatic colors where they help: HTTP status → null/blue, ≥500 red, ≥400 yellow, ≥300 blue, else green; log level → error red, warning yellow, info blue, else gray.

Examples

1// An exception with useful context — call it in a catch block
2try {
3 $gateway->charge($order);
4} catch (\Throwable $e) {
5 Dumpio::exception($e, ['order_id' => $order->id, 'user' => $user->email]);
6 throw $e; // re-throw; you only wanted to see it
7}
8 
9// A database query with its bindings and time (ms)
10Dumpio::query('select * from orders where status = ?', ['paid'], 12.4);
11 
12// An outgoing HTTP call — the color comes from the status
13Dumpio::http('POST', 'https://api.stripe.com/v1/charges', 402, [
14 'responseTime' => 240,
15 'body' => ['error' => 'card_declined'],
16]);
17 
18// A quick table
19Dumpio::table(['id', 'status'], [[1, 'paid'], [2, 'pending']], ['message' => 'Orders']);
20 
21// "How did the code even reach this line?"
22Dumpio::trace('unexpected branch');

For how each of these renders (and how exception parsing works), see Dump types and Exceptions.

Short global helpers

The most common typed helpers have plain global shortcuts, autoloaded like dio():

1dumpio_exception($e, ['user' => ['id' => 1]]); // = Dumpio::exception(...)
2dumpio_query('select * from users', [], 1.2); // = Dumpio::query(...)
3dumpio_trace('reached checkout'); // = Dumpio::trace(...)
4dumpio_memory('after import'); // = Dumpio::memory(...)

Recipes

Small, real-world patterns you'll reach for most.

1// Inspect one branch of a request
2dio($request->all(), 'payload')->channel('checkout')->yellow();
3 
4// Count how often a condition fires (one live-updating "×N" entry)
5foreach ($lines as $line) {
6 if ($line->isDiscounted()) {
7 dio($line)->count('discounted lines');
8 }
9}
10 
11// Profile a block with splits
12$sw = Dumpio::stopwatch('checkout');
13$cart = $this->buildCart($request); $sw->lap('cart built');
14$order = $this->placeOrder($cart); $sw->stop('order placed');
15 
16// Debug only when a flag is present (safe on shared/staging)
17dio($internalState)->when(config('app.debug'))->channel('internals');
18 
19// Tap inside an expression without changing the result
20return response()->json(dumpio($payload, 'response'));

Configuration

You usually don't need to configure anything. When you do, either set environment variables or call Dumpio::configure([...]) once during boot:

Option Env var Default Meaning
host DUMPIO_HOST localhost Where the Dumpio app listens
port DUMPIO_PORT 21234 The port it listens on
token DUMPIO_TOKEN "" Shared token, if the app requires one
enabled DUMPIO_DISABLE (set it ⇒ off) true Master on/off switch
timeoutMs 1500 Network timeout in milliseconds
maxDepth / maxItems / maxString 6 / 100 / 2000 How deep/wide/long the value tree may get
1Dumpio::configure([
2 'host' => '127.0.0.1',
3 'port' => 21234,
4 'token' => 'Dio-…', // only if the app has a token set
5]);

The token is sent as the X-Dumpio-Token header. The package talks HTTP only.

Running your app in Docker or a VM? localhost inside the container isn't your host machine. Point DUMPIO_HOST at the host (often host.docker.internal) and make sure the Dumpio server accepts non-loopback connections.

What actually gets sent (the wire format)

You rarely need this, but it helps to know there's no magic. Each dump is a small JSON object with a standard envelope plus type-specific fields:

1{
2 "timestamp": 1749900000000,
3 "flag": "blue",
4 "channel": "default",
5 "type": "var",
6 "language": "php",
7 "label": "current user",
8 "caller": { "file": "/app/Http/UserController.php", "line": 21, "function": "show" },
9 "value": {
10 "kind": "array",
11 "children": [
12 { "kind": "int", "value": 7, "key": "id" },
13 { "kind": "string", "value": "Ada", "key": "name" }
14 ]
15 }
16}

The serializer understands scalars, arrays, objects (with class and property visibility), enums, DateTime, Eloquent models, Laravel and Doctrine collections, Symfony UID (Uuid/Ulid), SplObjectStorage, and Stringable value objects (their __toString() is shown alongside the real properties), detects cyclic references, and trims by maxDepth/maxItems/maxString. The framework-aware shapes are detected by class name, so they stay inert when the framework isn't installed.

Production safety

The package is built to be safe to leave installed:

  • Off in production by default. The standalone client is enabled unless DUMPIO_DISABLE is set; under Laravel, enabled follows APP_DEBUG. A normal production deploy sends nothing.
  • Never throws. Every public method swallows its own errors — a missing app, a serialization edge case, or a network hiccup is silently ignored.
  • Bounded work. The serializer caps depth/items/string length, so even a huge object graph does limited work.

Using it with a framework

The framework integrations add automatic capture — queries, exceptions, emails, jobs, and more — with zero code changes, plus conveniences like chainable macros. They have their own pages:

  • PHP + Laravel — auto-discovered service provider, config/dumpio.php, the ->dio() macros, Monolog handler, Livewire, and more.
  • PHP + Symfony — the Dumpio bundle, Doctrine/Messenger/Mailer capture, and the exception subscriber.

Troubleshooting

Nothing shows up.

  1. Is the Dumpio app running and listening on the configured port? Test it directly: curl http://127.0.0.1:21234/health should return {"ok":true,…}.
  2. Is the client enabled? Standalone: DUMPIO_DISABLE must be unset. Laravel: DUMPIO_ENABLED=true (or APP_DEBUG=true).
  3. Confirm the transport independently: echo '{"message":"ping"}' | nc localhost 21234.
  4. Custom host/port? Make sure DUMPIO_HOST/DUMPIO_PORT match the app.

A token is set. Give the client the same value with DUMPIO_TOKEN; it's sent as the X-Dumpio-Token header.

A dump looks cut off. You hit maxDepth / maxItems / maxString. Raise them via Dumpio::configure([...]) for that run.

For app-side issues (403/413/429, the red raw entry, filters hiding dumps), see the app's Troubleshooting page.