The SplObjectStorage
class allows storing additional data about objects. Objects are stored as keys and associated data as values. In other words, this class allows creating a map from objects to data. The objects that are used as keys are strongly referenced. This means if an object falls out of scope and is not used anywhere, a SplObjectStorage
prevents the garbage collector to clean up this object. This can cause a memory leak.
We created an instance of the SplObjectStorage
class. One object was added to the map. Object gets destructed by using unset
function. However, garbage collector cannot clean up this object because it still used in the SplObjectStorage
.
<?php
$map = new SplObjectStorage();
$object = new stdClass();
$map[$object] = 'Data';
echo count($map); // 1
unset($object);
echo count($map); // 1
Since PHP 8.0, we can use WeakMap
class that allows to create a map from objects to data like a SplObjectStorage
does. However, the objects that are used as keys are weakly referenced. If an object goes out of scope, a WeakMap
don't prevent this object to be cleaned by garbage collector.
We created an instance of the WeakMap
class. When an object gets destructed, it will be cleaned by garbage collector and removed from the WeakMap
.
<?php
$map = new WeakMap();
$object = new stdClass();
$map[$object] = 'Data';
echo count($map); // 1
unset($object);
echo count($map); // 0
A WeakMap
can be used to implement cache.
Let's say we have two classes: Product
and Supplier
.
<?php
class Product {}
<?php
class Supplier {}
We created a ProductRepository
class that has findProductsBySupplier
method. This method returns products by supplier. If the products for the given supplier are already loaded, they are retrieved from the $cache
property instead of loading from database or other storage. The getCacheSize
method is used for testing to check cache size.
<?php
class ProductRepository
{
private WeakMap $cache;
public function __construct()
{
$this->cache = new WeakMap();
}
public function getCacheSize(): int
{
return count($this->cache);
}
public function findProductsBySupplier(Supplier $supplier): array
{
return $this->cache[$supplier] ??= $this->loadProductsBySupplier($supplier);
}
private function loadProductsBySupplier(Supplier $supplier): array
{
// Method implementation ...
return [
new Product(),
new Product(),
];
}
}
When the Supplier
object is destructed, the garbage collector removes the object key and all related products (values) from the cache.
<?php
require_once 'Product.php';
require_once 'Supplier.php';
require_once 'ProductRepository.php';
$repository = new ProductRepository();
$supplier = new Supplier();
$products = $repository->findProductsBySupplier($supplier);
echo $repository->getCacheSize(); // 1
unset($supplier);
echo $repository->getCacheSize(); // 0
Leave a Comment
Cancel reply