zendframework/zend-memory
Zend Memory provides PHP memory-management utilities, including a memory manager, memory objects, and storage containers with limit and sliding-window strategies. Useful for controlling memory in long-running processes and cache-like workflows.
composer require laminas/laminas-memory — zendframework/zend-memory is archived (last release 2019-10-16) and superseded by the Laminas fork. If you must stay on the Zend package, pin the version and accept abandoned-package warnings.Memory, Storage\Adapter\File, Storage\Adapter\Sqlite, and Container\Movable / Container\Locked.memory_limit.use Zend\Memory\Memory;
use Zend\Memory\Storage\Adapter\File as FileStorage;
$memory = Memory::factory([
'memory_limit' => '64MB', // explicit; do not trust ini in CLI
'min_memory_size' => 16 * 1024, // don't swap trivially small values
'storage' => new FileStorage('/var/tmp/zend-memory'),
]);
$container = $memory->create($bigString); // Movable: may be swapped to disk
$container->getValue(); // read (may load from storage)
$container->setValue($updated); // write (may swap back/forth)
$container->getSize(); // serialized size in bytes
Without a storage adapter nothing can be swapped, and the component degrades to a thin wrapper around your values.
Budgeted batch processing. One Memory instance per worker process; wrap the large intermediates.
$chunks = [];
foreach (array_chunk($records, 500) as $i => $chunk) {
$chunks[$i] = $memory->create(json_encode($chunk));
}
// Later, touch only what you need:
$slice = json_decode($chunks[3]->getValue(), true);
Locked vs movable. $memory->create($value, true) returns a Locked container that is never swapped. Keep your hot working set locked and everything archival movable, otherwise you pay storage I/O on every iteration.
One container per unit, not one giant container. A container is swapped as a whole. Many medium containers spread across the budget will spill incrementally; a single monolithic container either fits or thrashes.
Generator-driven pipelines. Pair the component with streaming readers (XMLReader, SplFileObject, generators) that yield containers rather than raw strings. Downstream code then never materializes the full dataset and memory stays flat.
Custom storage adapter. Extend Zend\Memory\Storage\Adapter\AbstractAdapter (mirror the File adapter's load/save/remove methods) to spill to Redis, S3, or APCu instead of local disk. Register it through the storage option.
Queue workers. Instantiate Memory per job and unset() it at the end so the storage backend is released and temp artifacts are cleaned up. Long-lived workers should recreate the instance periodically.
Archived and superseded. This package is abandoned; laminas/laminas-memory is the maintained continuation. The namespace shifts from Zend\Memory to Laminas\Memory and the API is essentially identical, so plan the migration rather than building new code on the Zend namespace.
CLI memory limit. memory_limit is commonly -1 under CLI, so auto-detection returns false and swapping never triggers. Always pass an explicit memory_limit in the factory options for workers.
min_memory_size silently disables swapping. Values below the threshold stay resident permanently. That is intentional anti-thrashing behavior, but it surprises people who set it high and then wonder why memory keeps growing.
Serialization constraints. Values must be serializable — strings, scalars, arrays, plain objects. Resources, closures, and PDO handles fail when the container is written to storage. Prefer strings (e.g. json_encode) for predictable sizing and cheap reloads.
Size accounting is approximate. getSize() measures the serialized value, not PHP's real footprint. Treat the budget as a heuristic and leave headroom below the actual memory_limit.
Concatenation inside a container is a trap. $c->setValue($c->getValue() . $more) reads and rewrites the whole container each iteration — O(n²) I/O once it has been swapped. Accumulate in a local string and set once, or use multiple containers.
Cleanup and disk choice. File/Sqlite adapters clean up on destruction, but fatal errors and exit leave temp files behind. Sweep the storage directory at worker start and keep it on a fast local disk, not NFS.
Sqlite contention. The Sqlite adapter holds a database file per store; parallel workers writing the same path can lock. Give each process its own directory or file.
Don't confuse with global metrics. Memory::getMemoryUsage() reflects the component's view of container memory, not memory_get_usage(). Compare against the real function when tuning budgets.
The extension point is the storage adapter. That is the seam for compression, encryption, or a different spill target — everything else is effectively fixed.
Verify on modern PHP. The final release predates PHP 8.1+, so check for deprecation/type issues before adopting it in a current stack — one more reason to move to the Laminas fork.
How can I help you explore Laravel packages today?