PHP 8 Weak Maps and Practical Use Cases

Published On06 Nov 2020

Practical Use Cases of PHP 8 Weak Maps

PHP 8.0 brings Weak Maps to PHP. Weak Maps are data structures that can hold objects, but allows them to be cleared with garbage collection.

A post with the WeakMap class synopsis, restrictions, and its semantic is at PHP 8.0: Weak Maps.

Weak Maps takes the PHP 7.4 Weak References to a new level, and introduces collections of references that can be garbage collected if there are no other references to the object. Weak Maps can help minimize and efficiently manage memory.

Most of the PHP applications are request-based, and memory is automatically cleared at the end of the request. Async PHP approaches, and long-lived applications, however, need to keep an eye on potential memory leaks. WeakMap class instances can work as a clever and easy work-around to the object life-cycle management.

How Weak Maps Work

Weak Maps basically function as a key-value store, where keys are objects, and values can be arbitrary data. The key is weakly referenced in the key-value store, and if the key falls out of scope, the weak map key-value store does not prevent the key object from being garbage collected.

A Weak Map store additional data against the object key, without changing the state of the key object itself, or objecting its garbage collection.


PHP's splObjectStorage class allows to store additional data for an object key.

$obj1 = new stdClass();
$obj->name = 'Foo'; $obj->age = 28;

$map = new splObjectStorage();

$map[$obj1] = 'Additional data for stdClass #1';

Weakmap - splObjectStorage - using reference

The key is strongly referenced, and prevents them from being garbage collected.


If the $obj1 falls out of scope, for example they are being unset($obj1), PHP's garbage collector cannot clean those objects because they are still referenced in the $map splObjectStorage object.

$obj1 = new stdClass();
$obj->name = 'Foo'; $obj->age = 28;

$map = new splObjectStorage();

$map[$obj1] = 'Additional data for stdClass #1';

unset($obj1); // Only clears reference

count($map); // 1

Weakmap - Using Weakmap with reference


Weak Maps is a key-value store similar to splObjectStorage, where keys are objects. However, the references are weak, as in they allow the garbage collector to clear out the values.

$obj1 = new stdClass();
$obj->name = 'Foo'; $obj->age = 28;

$map = new WeakMap();

$map[$obj1] = 'Additional data for stdClass #1';

Weakmap - Using Weakmap with reference


The benefit of Weak Map is that, if the object falls out of scope, the garbage collector can immediately remove it from the Weak Map. When the key object is removed (garbage collected), its associated value will be removed from the Weak Map as well.

$obj1 = new stdClass();
$obj->name = 'Foo'; $obj->age = 28;

$map = new WeakMap();

$map[$obj1] = 'Additional data for stdClass #1';

unset($obj1); // Removes $obj and the key and value from $map as well.

count($map); // 0

Weakmap - Using Weakmap, object destroyed


Practical Use Cases of PHP Weak Maps

Weak Maps use objects as keys. A Weak Map might be an ideal candidate to store associated data for a given object. When the object is unset, or falls out of scope, Weak Map makes sure that the additional data is cleared too.

This provides a volatile cache storage without Lapsed listener problems, that the cached data does not cause a memory leak either by storing the objects preventing the garbage collector, and by storing additional data for objects no longer necessary.

In Caching

Weak Maps help create temporary cache, where the life-cycle of the cache keys (objects) and values (arbitrary data) is managed automatically when the key falls out of scope.

The following example is for a sample CommentRepository class, that is responsible to load comments of a given Post object.

CommentRepository can make use of a Weak Map to store the cached data, and prevent database calls if the comments are already retrieved for the given post.

class CommentRepository {
    private WeakMap $comments_cache;

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

    public function getCommentsByPost(Post $post): ?array {
        if (!isset($this->comments_cache[$post])) {
            $this->comments_cache[$post] = $this->loadComments($post);
        }
        return $this->comments_cache[$post]
    }
}

private WeakMap $comments_cache snippet is using PHP 7.4 typed properties.

$comments_cache property stores comments key'd by the Post objects. This reference does not count when PHP garbage collector attempts to clear a Post object. When a Post object is cleared, all its associated comment items (values) will be automatically removed as well.

  • If an array is used for the cache storage, the key must be a string or an int, which requires a unique key inside the Post object.
  • If splObjectStorage is used for the cache storage, they will not be removed when the Post object is unset.

A few calls to load comments might not make a noticeable difference. However, if the CommentRepository::getCommentsByPost is called several times, a strongly referenced cache would have caused a memory leak due to the strong reference to each Post object and its loaded values.

With a Weak Map, only the alive Post objects and their comments are stored in the map.

In associating auxiliary object data

Another use case of Weak Maps is storing auxiliary data for objects without storing them in the object itself.

Due to the weak references in a Weak Map, it is possible to associate auxiliary data for an object, and have them removed automatically when the object is unset.

class EmailSender {
    private WeakMap $counter;

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

    public function send(Account $account, EmailMessage $message) {
        $this->counter[$account] ??= 0;
        $this->counter[$account]++;

        // Do the rest.
    }

    public function getStats(): iterable {
        return $this->counter->getIterator();
    }

}

$this->counter[$account] ??= 0; snippet is using the Null Coalescing Assignment operator

With the example above, every-time the EmailSender::send method is called, it counts the number of times an email is sent to each Account object.

  • The number of emails sent does not belong as a property to the Account objects.
  • The EmailSender::getStats method returns an iterable to use with foreach, and can list the number of each account is emailed.
  • If an Account object falls out of scope of the parent caller, or unset anywhere in the code, the counter will be removed as well.

WeakMap objects support iterating with foreach, or retrieving an iterable object from the getIterator method.


Read More

Recent Articles on PHP.Watch

All ArticlesFeed 
PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

A guide for Debian and Ubuntu on how to install PHP 8.4 on a new server or how to upgrade an existing PHP setup to PHP 8.4.
How to fix `mysql_native_password` not loaded errors on MySQL 8.4

How to fix mysql_native_password not loaded errors on MySQL 8.4

How to fix the SQLSTATE[HY000] [1524] Plugin 'mysql_native_password' is not loaded errors caused in MySQL 8.4 no longer enabling the mysql_native_password plugin by default.
How to fix PHP Curl HTTPS Certificate Authority issues on Windows

How to fix PHP Curl HTTPS Certificate Authority issues on Windows

On Windows, HTTPS requests made with the Curl extension can fail because Curl has no root certificate list to validate the server certificates. This article discusses the secure and effective solutions, and highlights bad advice that can leave PHP applications insecure.
Subscribe to PHP.Watch newsletter for monthly updates

You will receive an email on last Wednesday of every month and on major PHP releases with new articles related to PHP, upcoming changes, new features and what's changing in the language. No marketing emails, no selling of your contacts, no click-tracking, and one-click instant unsubscribe from any email you receive.

Support PHP.Watch — If you find the articles, version information, Codex, and other PHP.Watch contributions useful, consider supporting through GitHub Sponsors. Your sponsorship helps dedicate more time to creating valuable content and improving the PHP community. Together, we can keep the momentum going — thank you for your support!

Thanks to the highest tier sponsor: @TomasVotruba for your generous support to keep PHP.Watch moving 💜