php-references-lab

References Are NOT Pointers — CTF Edition

v1.0 · standalone
lab output
zval lifecycle — step by step
Press a step below to start...
progress:
01 Pointer vs Reference — what is the actual difference?
AspectPointer (C / C++)Reference (PHP)
StorageVariable that holds a memory address like 0x7fff...Just another name bound to the same zval
After deletionDangling pointer — undefined behaviour, potential crashAll remaining aliases keep working perfectly
ArithmeticPointer arithmetic possible (ptr+1, ptr-2)Impossible — no access to raw addresses
Memory managementManual — you must call free()Automatic — refcount GC handles everything
02 The zval and refcount

Every PHP variable is backed internally by a zval (Zend Value Structure). A zval contains three key fields:

value — the actual stored data
refcount — how many names are currently bound to this zval
is_ref — whether an explicit reference binding exists

When you write $b = &$a, no copy is made. Both names point to the same zval and refcount increments by 1.

When you call unset($a), refcount drops by 1 only. The zval is freed from memory only when refcount reaches exactly 0.

03 The foreach reference trap

One of the most common hard-to-spot bugs in PHP production code:

// The bug foreach ($arr as &$v) { $v++; } // $v is STILL an alias for $arr[last] here! foreach ($arr as $v) { /* each iteration overwrites $arr[last] through $v */ } // The fix — always unset() after a reference loop foreach ($arr as &$v) { $v++; } unset($v); // break the alias immediately
04 Objects — identifier handle, not a true reference

A common misconception: "objects are passed by reference automatically." This is not accurate.

PHP passes an Object Identifier — an internal handle number. The identifier is copied, not the object itself.

So if you do $b = $a and mutate $b->x, it affects $a->x too because both identifiers resolve to the same object. But if you reassign $b = new Foo(), only $b's binding changes — $a is completely unaffected.

If you want a true reference to the variable slot itself: $b = &$a