WeakMap (PHP 8.0)
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7), memory leaks from object-keyed caches typically surface via memory profiling or code review; PHPStan won't flag SplObjectStorage misuse as a GC issue.
Closest to 'simple parameterised fix' (e3), swap SplObjectStorage/array cache for WeakMap — a localised pattern replacement, though may need key-existence checks added.
Closest to 'localised tax' (b3), WeakMap usage is typically confined to specific cache/memoization components and doesn't shape the broader system.
Closest to 'notable trap' (t5), misconception conflates WeakMap with WeakReference, and the iteration-during-GC gotcha plus object-only key restriction are documented surprises most devs learn the hard way.
Also Known As
TL;DR
Explanation
A WeakMap holds object keys weakly — when the only remaining reference to a key object is in the WeakMap, that object is garbage collected and its WeakMap entry is automatically removed. This contrasts with SplObjectStorage and arrays, which hold strong references and prevent GC. Typical use cases: per-object caches (memoising computed values on an entity without modifying the entity class), attribute caches in ORMs, and proxy metadata storage. WeakMap is iterable and Countable. PHP 8.0+; the RFC was specifically motivated by Doctrine's need to associate metadata with entities without preventing their deallocation.
Common Misconception
Why It Matters
Common Mistakes
- Using a regular array or SplObjectStorage for object-keyed caches — holds strong references, preventing GC.
- Not checking if a WeakMap key still exists before access — the object may have been collected.
- Using WeakMap for non-object keys — it only accepts objects as keys.
- Not realising WeakMap is not iterable when the GC runs between iterations — do not iterate a WeakMap.
Code Examples
// Regular array cache — prevents GC of $user objects:
class Cache {
private array $data = [];
public function set(object $key, mixed $val): void {
$this->data[spl_object_id($key)] = $val; // Strong reference via ID
}
}
// WeakMap — GC can collect $user when no other references exist:
$cache = new WeakMap();
$cache[$user] = computeExpensiveData($user); // Freed when $user is GC'd
$cache = new WeakMap();
function expensiveData(object $obj, WeakMap $cache): array {
return $cache[$obj] ??= computeExpensiveData($obj);
// Entry removed automatically when $obj is garbage collected
}