Standards Support

PSR-6

Overview

The Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator provides a PSR-6 compliant wrapper for supported storage adapters.

PSR-6 specifies a common interface to cache storage, enabling developers to switch between implementations without having to worry about any behind-the-scenes differences between them.

Quick Start

To use the pool, instantiate your storage as normal, then pass it to the CacheItemPoolDecorator.

use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;use Laminas\Cache\Service\StorageAdapterFactoryInterface;use Psr\Container\ContainerInterface;

/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

$storage = $storageFactory->create('apc');

$pool = new CacheItemPoolDecorator($storage);

// attempt to get an item from cache
$item = $pool->getItem('foo');

// check whether item was found
if (! $item->isHit()) {
    // ...
    // perform expensive operation to calculate $value for 'foo'
    // ...

    $item->set($value);
    $pool->save($item);
}

// use the value of the item
echo $item->get();

Note that you will always get back a CacheItem object, whether it was found in cache or not: this is so false-y values like an empty string, null, or false can be stored. Always check isHit() to determine if the item was found.

Supported Adapters

The PSR-6 specification requires that the underlying storage support time-to-live (TTL), which is set when the item is saved. For this reason the following adapters cannot be used: Dba, Filesystem, Memory and Session. The XCache adapter calculates TTLs based on the request time, not the time the item is actually persisted, which means that it also cannot be used.

In addition adapters must support the Laminas\Cache\FlushableInterface. All the current Laminas\Cache\Storage\Adapters fulfil this requirement.

Attempting to use an unsupported adapter will throw an exception implementing Psr\Cache\CacheException.

The Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator adapter doesn't support driver deferred saves, so cache items are saved on destruct or on explicit commit() call.

Quirks

APC

You cannot set the apc.use_request_time ini setting with the APC adapter: the specification requires that all TTL values are calculated from when the item is actually saved to storage. If this is set when you instantiate the pool it will throw an exception implementing Psr\Cache\CacheException. Changing the setting after you have instantiated the pool will result in non-standard behaviour.

Logging Errors

The specification states:

While caching is often an important part of application performance, it should never be a critical part of application functionality. Thus, an error in a cache system SHOULD NOT result in application failure.

Once you've got your pool instance, almost all exceptions thrown by the storage will be caught and ignored. The only storage exceptions that bubble up implement Psr\Cache\InvalidArgumentException and are typically caused by invalid key errors. To be PSR-6 compliant, cache keys must not contain the following characters: {}()/\@:. However different storage adapters may have further restrictions. Check the documentation for your particular adapter to be sure.

We strongly recommend tracking exceptions caught from storage, either by logging them or recording them in some other way. Doing so is as simple as adding an ExceptionHandler plugin. Say you have a PSR-3 compliant logger called $logger:

use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Psr\Container\ContainerInterface;

$cacheLogger = function (\Exception $e) use ($logger) {
    $message = sprintf(
        '[CACHE] %s:%s %s "%s"',
        $exception->getFile(),
        $exception->getLine(),
        $exception->getCode(),
        $exception->getMessage()
    );
    $logger->error($message);
};

/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);              

$storage = $storageFactory->create(
    'apc', 
    [], 
    [
        [
            'name' => 'exceptionhandler',
            'options' => [
                'exception_callback' => $cacheLogger,
                'throw_exceptions' => true,
            ],
        ],
    ]
);

$pool = new CacheItemPoolDecorator($storage);

Note that throw_exceptions should always be true (the default) or you will not get the correct return values from calls on the pool such as save().

Supported Data Types

As per the specification, the following data types can be stored in cache: string, integer, float, boolean, null, array, object and be returned as a value with exactly the same type.

Not all adapters can natively store all these types. For instance, Redis stores booleans and integers as a string. Where this is the case all values will be automatically run through serialize() on save and unserialize() on get: you do not need to use a Laminas\Cache\Storage\Plugin\Serializer plugin.

By default, the PSR-6 cache decorator uses the systems time to determine cache expirations. This can be changed by passing a stella-maris/clock Clock implementation to the CacheItemPoolDecorator#__construct. By doing so, the method CacheItemInterface#expiresAfter uses that Clock to calculate the cache items TTL when passing a DateInterval instance.

use Lcobucci\Clock\SystemClock;
$clock = new SystemClock('Antarctica/Troll');
$pool = new CacheItemPoolDecorator($storage, $clock);

$item = $pool->getItem('non-existent-key');
$item
    ->set('foo')
    ->expiresAfter(DateInterval::createFromDateString('24 hours'));

// Saves the item which expires in 24h
$pool->save($item);