Using PHP 8 WeakMap to Associate Data with Objects

PHP 8 introduced the WeakMap class, a powerful feature that allows developers to associate arbitrary data with objects without preventing those objects from being garbage collected. This article provides a practical guide on how to use WeakMap to manage object-linked data, prevent memory leaks, and write more efficient PHP applications.

What is a WeakMap?

A WeakMap is a collection (map) where keys are objects, and values can be any type of data. Unlike a standard array or SplObjectStorage—which hold “strong” references to their keys—a WeakMap holds “weak” references.

When an object used as a key in a WeakMap has no other active references remaining in your application, PHP’s garbage collector automatically destroys the object and removes its associated entry from the WeakMap.

How to Use WeakMap

Using a WeakMap is straightforward as it implements the ArrayAccess, Countable, and IteratorAggregate interfaces. This means you can interact with it just like a regular array.

Basic Implementation Example

Here is a simple example showing how to instantiate a WeakMap, associate data with an object, and retrieve it:

class User {
    public function __construct(public string $username) {}
}

// 1. Initialize the WeakMap
$loginHistory = new WeakMap();

$user1 = new User('alice');
$user2 = new User('bob');

// 2. Associate data with the objects
$loginHistory[$user1] = ['last_login' => '2023-10-01', 'ip' => '192.168.1.1'];
$loginHistory[$user2] = ['last_login' => '2023-10-02', 'ip' => '192.168.1.2'];

// 3. Retrieve the associated data
echo $loginHistory[$user1]['last_login']; // Outputs: 2023-10-01

Automatic Garbage Collection

The primary benefit of WeakMap is automatic cleanup. If you unset or lose reference to the key object, the memory is freed instantly.

// Check current count
echo count($loginHistory); // Outputs: 2

// Destroy one of the user objects
unset($user1);

// The associated entry in WeakMap is automatically removed
echo count($loginHistory); // Outputs: 1

If you had used a standard array with object hashes (spl_object_hash) or SplObjectStorage to store this cache, user1 would remain in memory until the map itself was destroyed, resulting in a memory leak.

Common Use Cases

1. Caching and Memoization

WeakMap is ideal for caching computed results derived from objects (like database entities). If the entity is destroyed, the cache is automatically cleared.

class InvoiceCalculator {
    private WeakMap $cache;

    public function __construct() {
        $this->cache = new WeakMap();
    }

    public function calculateTotal(Invoice $invoice): float {
        if (!isset($this->cache[$invoice])) {
            // Simulate heavy calculation
            $this->cache[$invoice] = $invoice->getSubTotal() * 1.21;
        }

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

2. Attaching Metadata in Long-Running Processes

For daemon-like PHP environments (such as Swoole, RoadRunner, or ReactPHP queue workers), keeping memory usage low is critical. WeakMap allows you to attach temporary state, event listeners, or metadata to request and connection objects without worrying about manual teardown procedures.