Weak maps do not exist yet; spl_object_id and a sweep

A registry keyed by object identity keeps every object it has ever seen alive, so a long-running worker grows in memory with the number of distinct objects it has processed.

final class MetadataCache
{
    private array $byId = [];
    private array $refs = [];

    public function put(object $o, array $meta): void
    {
        $id = spl_object_id($o);

        $this->byId[$id] = $meta;
        $this->refs[$id] = WeakReference::create($o);
    }

    public function sweep(): void
    {
        foreach ($this->refs as $id => $ref) {
            if ($ref->get() === null) {
                unset($this->byId[$id], $this->refs[$id]);
            }
        }
    }
}

WeakMap arrives in 8.0 and does all of this in one class, so the sweep and the two parallel arrays are a 7.4 workaround with a deadline. The detail that makes the sweep necessary rather than optional is that spl_object_id values are reused after collection, so a stale entry can be attributed to a completely different object — a bug that produces wrong data rather than a leak. SplObjectStorage is the obvious alternative and holds strong references, which is exactly the problem being solved.