A cache that stores objects in an array keeps every one alive, because the array holds a reference. In a request that is a slow leak; in a worker that runs for hours (FrankenPHP and Worker Mode) it ends in a crash. A WeakMap (PHP 8.0) uses objects as keys without counting as a reference, and an entry vanishes when its key object is destroyed. A WeakReference (PHP 7.4) holds one object the same way, and its get() returns null once the object is gone.
<?php
final class Product { public function __construct(public readonly int $id) {} }
final class PriceCache
{
public WeakMap $prices;
public function __construct() { $this->prices = new WeakMap(); }
public function price(Product $p): float { return $this->prices[$p] ??= $p->id * 1.5; }
}
$cache = new PriceCache();
$products = array_map(fn(int $i): Product => new Product($i), range(1, 50_000));
array_walk($products, fn(Product $p) => $cache->price($p));
printf("%6d cached, %.1f MB\n", count($cache->prices), memory_get_usage() / 2 ** 20);
$products = []; // the page is done with them
printf("%6d cached, %.1f MB\n", count($cache->prices), memory_get_usage() / 2 ** 20);Output
50000 cached, 11.1 MB
0 cached, 5.9 MBUse a WeakMap to attach computed data to objects you do not own: a price per product, metadata per entity. Data keyed by an ID or a string still belongs in a size-capped array or in APCu or Redis 2,763 (APCu, Redis, Memcached).