What Exactly Is a WeakMap?

At a glance, it looks like just another key-value store. But here's the twist:

A WeakMap holds weak references to objects as keys—meaning if the object gets destroyed, the entry is automatically removed from the map.

In other words, it doesn't prevent objects from being garbage collected. So when an object is no longer in use elsewhere, PHP clears it out of the WeakMap for you. No manual cleanup, no memory leaks.

Why That Matters in Real Life

Imagine you're building something like a document editor or image manager.

Every object (say, a file or an image) has some expensive-to-compute metadata. You want to cache that metadata while the object is around, but you don't want to worry about manually cleaning things up.

Example;

<?php

class MetadataCache
{
    private WeakMap $cache;
    // private SplObjectStorage $cache; // Old way

    public function __construct()
    {
        $this->cache = new WeakMap();
        //$this->cache = new SplObjectStorage(); // It will still continue referencing
    }

    public function getMetadata(object $obj): array
    {
        if (!isset($this->cache[$obj])) {
            echo "Generating metadata for object...\n";
            $this->cache[$obj] = $this->generateMetadata($obj);
        } else {
            echo "Using cached metadata for object.\n";
        }

        return $this->cache[$obj];
    }

    private function generateMetadata(object $obj): array
    {
        // Simulate an expensive operation
        usleep(500000); // 0.5 seconds
        return [
            'class' => get_class($obj),
            'created_at' => date('c'),
            'object_id' => spl_object_id($obj),
            'hash' => spl_object_hash($obj),
        ];
    }
}

// Example class to represent a document or entity
class Document
{
    public string $title;

    public function __construct(string $title)
    {
        $this->title = $title;
    }
}

// Usage

$cache = new MetadataCache();

$doc1 = new Document("First Doc");
$doc2 = new Document("Second Doc");

print_r($cache->getMetadata($doc1)); // Will generate metadata
print_r($cache->getMetadata($doc1)); // Will use cached metadata
print_r($cache->getMetadata($doc2)); // Will generate metadata

print_r($cache);

unset($doc1); // After this, $doc1 is no longer referenced

print_r($cache);

Output:

Generating metadata for object...
Array
(
    [class] => Document
    [created_at] => 2025-04-12T04:23:51+00:00
    [object_id] => 3
    [hash] => 00000000000000030000000000000000
)
Using cached metadata for object.
Array
(
    [class] => Document
    [created_at] => 2025-04-12T04:23:51+00:00
    [object_id] => 3
    [hash] => 00000000000000030000000000000000
)
Generating metadata for object...
Array
(
    [class] => Document
    [created_at] => 2025-04-12T04:23:51+00:00
    [object_id] => 4
    [hash] => 00000000000000040000000000000000
)
MetadataCache Object
(
    [cache:MetadataCache:private] => WeakMap Object
        (
            [0] => Array
                (
                    [key] => Document Object
                        (
                            [title] => First Doc
                        )

                    [value] => Array
                        (
                            [class] => Document
                            [created_at] => 2025-04-12T04:23:51+00:00
                            [object_id] => 3
                            [hash] => 00000000000000030000000000000000
                        )

                )

            [1] => Array
                (
                    [key] => Document Object
                        (
                            [title] => Second Doc
                        )

                    [value] => Array
                        (
                            [class] => Document
                            [created_at] => 2025-04-12T04:23:51+00:00
                            [object_id] => 4
                            [hash] => 00000000000000040000000000000000
                        )

                )

        )

)
MetadataCache Object
(
    [cache:MetadataCache:private] => WeakMap Object
        (
            [0] => Array
                (
                    [key] => Document Object
                        (
                            [title] => Second Doc
                        )

                    [value] => Array
                        (
                            [class] => Document
                            [created_at] => 2025-04-12T04:23:51+00:00
                            [object_id] => 4
                            [hash] => 00000000000000040000000000000000
                        )

                )

        )

)

As we can see from the output, the MetadataCache object is no longer referencing the first document ($doc1).

However, if we didn't use WeakMap (for example, if we used SplObjectStorage instead), it would continue to hold a reference to $doc1 even after the unset operation.

None
None

Atakan Demircioğlu is a Full Stack Developer currently working at Jotform, a leading online form-builder platform.

He is passionate about blogging, learning, and creating. Sharing his experiences on Medium and GitHub.

Twitter: https://twitter.com/atakde

Github: https://github.com/atakde

Buy me a coffee if you like the content.