debug_zval_dump

Dumps a variable together with its internal reference count and storage details.

Signature
debug_zval_dump(mixed $value, mixed ...$values): void
  • Added in PHP 4.2.0

Parameters

ParameterTypeDescription
$valuemixedThe variable or value to dump.
$valuesoptionalmixedFurther variables or values to dump. Variadic — each argument is dumped in turn.

Return value

voidNo value is returned; the dump is written to the output stream.

Changelog

  • PHP 8.4.0The output now indicates whether an array is packed (sequential integer keys) or hashed.

var_dump plus the engine's bookkeeping

This prints the same type and value information as var_dump(), then appends the zval's internal state: refcount(n) for refcounted values, or interned for strings the compiler deduplicated. Since PHP 8.4 arrays also report whether they are packed — stored as a flat C array because the keys are sequential integers — or hashed, which is a real performance difference when you are optimising a hot loop.

When it is worth using

Almost never in application code. It earns its place when you are chasing memory growth and want to know whether a large array is genuinely being copied or merely shared under copy-on-write, or when you are writing a PHP extension and need to see what the engine actually holds. If you just want to see a value, use var_dump().

Reading the refcount honestly

The number is not the count in your calling scope. Arguments are passed by value, so the call itself adds a reference for the duration — the printed refcount is typically one higher than you expect. Scalars such as integers and booleans are not refcounted at all and show no count. References created with & are invisible here, because only the value reaches the function, not the reference wrapper. Treat the number as a relative signal — did it go up, did it drop after a write — rather than an absolute truth.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Confirm that assigning a large array to another variable shares memory instead of copying it, while hunting a memory spike.
  • Check whether a string built at runtime is interned or a separate allocation before caching millions of them.
  • Verify in PHP 8.4+ that a hot-path array is still packed after the keys have been manipulated.

Frequently asked questions

What is the difference between debug_zval_dump and var_dump?

debug_zval_dump() prints everything var_dump() does plus the zval's refcount, interned flag, and (since PHP 8.4) whether an array is packed. Use var_dump() for ordinary debugging and debug_zval_dump() only when you care about memory sharing.

Why is the refcount higher than the number of variables I have?

Because the value is passed to the function by value, which adds one reference for the duration of the call. Compare refcounts across dumps rather than reading any single number as the true count.

Why does debug_zval_dump show no refcount for my integer?

Integers, floats, booleans and null are not refcounted in PHP — the value is stored inline in the zval, so there is nothing to count. Only strings, arrays, objects and resources carry a refcount.

More examples

Copy-on-write, made visible

Before the write, both variables point at one array and the refcount reflects the sharing. Appending to $copy separates them, and the refcount on $original drops — this is the moment PHP actually spends the memory, which is what you are looking for when an array assignment seems suspiciously cheap.

Example · php
<?php
$original = range(1, 5);
$copy = $original;

echo "shared:\n";
debug_zval_dump($original);

$copy[] = 6;   // the write forces a separation

echo "after writing to \$copy:\n";
debug_zval_dump($original);
echo count($original), ' vs ', count($copy), "\n";

Interned strings versus runtime strings

The literal is marked interned: the compiler stored one shared, immortal copy, so no refcount is tracked. The concatenated string is a real allocation with a refcount. Booleans, null and floats print no refcount at all because those types are not refcounted.

Example · php
<?php
debug_zval_dump('compile time literal');

$suffix = 'string';
$runtime = 'runtime ' . $suffix;
debug_zval_dump($runtime);

debug_zval_dump(true, null, 3.5);

Why the number is always one too high

Three dumps of the same string with one holder, two holders, then one again. The absolute values look off by one because passing the argument adds a temporary reference; the deltas between the three lines are the part you can trust.

Example · php
<?php
$a = str_repeat('z', 8);
debug_zval_dump($a);       // only $a holds it, plus the call itself

$b = $a;
debug_zval_dump($a);       // one more holder

unset($b);
debug_zval_dump($a);       // back down again

Discussion

  • Be the first to comment on this lesson.