yiisoft/cache
A lightweight PSR-16 cache package for PHP from the Yii3 ecosystem, offering a simple, consistent API for storing and retrieving values with TTL support and a foundation for file, array, APCu, and other cache backends.
Install the core package:
composer require yiisoft/cache
Start with these files:
src/Cache.php — decorator around a PSR-16 store; exposes getOrSet() for lazy population.src/CacheInterface.php — Yii’s extended PSR-16 contract.src/ArrayCache.php — in-memory store, ideal for tests and request-scoped caching.src/NullCache.php — no-op store for disabling cache in local/debug environments.First day-to-day use case: satisfy a third-party library that type-hints Psr\SimpleCache\CacheInterface without pulling in Symfony Cache or writing your own PSR-16 adapter.
Minimal Laravel binding:
use Psr\SimpleCache\CacheInterface;
use Yiisoft\Cache\Cache;
use Yiisoft\Cache\ArrayCache;
$this->app->singleton(CacheInterface::class, function () {
return new Cache(new ArrayCache());
});
Then inject it normally:
public function __construct(private CacheInterface $cache) {}
public function lookup(string $id): array
{
return $this->cache->getOrSet("lookup:$id", fn () => $this->query($id), 300);
}
Treat the core package as the PSR-16 contract/decorator layer, not as a full Laravel cache replacement. For persistent storage, add a driver package such as yiisoft/cache-file, yiisoft/cache-redis, or yiisoft/cache-apcu.
Use it as an anti-corruption layer for PSR-16 dependencies. If an SDK, HTTP client, or domain package requires Psr\SimpleCache\CacheInterface, bind yiisoft/cache rather than leaking Laravel’s Illuminate\Contracts\Cache\Repository into that package.
Prefer getOrSet() for read-through caching. It keeps call sites compact and makes the cache key the obvious unit of ownership:
$settings = $cache->getOrSet('settings:tenant:'.$tenantId, function () use ($tenantId) {
return $this->settings->forTenant($tenantId);
}, 600);
Use ArrayCache in tests. Because it is in-memory and PSR-16-compliant, you can assert cache hits/misses without touching Redis or the filesystem:
$cache = new Cache(new ArrayCache());
$service = new LookupService($cache);
Use NullCache for local or feature-flagged environments. Bind NullCache when you want the same code path but no persistence and no stale data during debugging.
Bridge to Laravel’s cache only where necessary. If you need Laravel’s Redis/file stores, write a small PSR-16 adapter around Illuminate\Contracts\Cache\Repository, then wrap it with Yiisoft\Cache\Cache to get getOrSet():
final class LaravelPsr16Adapter implements CacheInterface
{
public function __construct(private Repository $store) {}
public function get(string $key, mixed $default = null): mixed
{
return $this->store->get($key, $default);
}
public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
{
return $this->store->put($key, $value, $ttl);
}
// ...
}
Use a chain/decorator strategy for migration. For example: memory cache for hot keys in front of a persistent PSR-16 store, or a logging decorator that records misses during rollout. Keep decorators PSR-16-compliant.
Prefix keys explicitly for multi-tenant or multi-app use. There is no Laravel-style cache prefix configuration in the core package; build it into a key factory or decorator.
Keep serialization simple. Cache scalars, arrays, and DTOs that serialize cleanly. Avoid caching closures, resources, or objects with unserializable dependencies.
This is not Laravel’s cache. You do not get tags, rememberForever(), cache events, or Laravel’s key-prefix configuration. Use Laravel’s cache facade for those features; use yiisoft/cache for PSR-16 boundaries.
PSR-16 keys are stricter than Laravel keys. Reserved characters include {}()/\@: and keys should stay within 64 characters. Hash or normalize keys before passing them in:
$key = 'lookup:'.hash('xxh128', $rawKey);
TTL units are seconds, not minutes. PSR-16 accepts int, DateInterval, or null. A 0 or negative TTL generally means delete/expire, not “forever.” Pass null to use the adapter’s default TTL, and verify the behavior of the concrete driver.
The core package has no persistent driver. Binding ArrayCache in production gives you per-request caching only. Install and configure a driver package for file, Redis, APCu, or database storage.
No Laravel auto-discovery provider. You will usually register the binding yourself in AppServiceProvider or a dedicated package provider.
getOrSet() is not a distributed lock. Concurrent requests can still stampede the underlying query. For expensive recomputation, add a lock or use Laravel’s atomic locks around the callback.
Debugging misses is manual. Add a decorator that logs hits/misses and keys. There is no built-in profiler or cache event system.
Extension point: implement Psr\SimpleCache\CacheInterface for custom stores, then wrap it with Yiisoft\Cache\Cache if you want getOrSet(). Keep the adapter honest about TTL semantics and key validation.
Check driver-specific behavior before production. File cache serialization, Redis TTL handling, and APCu availability can differ. Test with the concrete driver you will deploy, not only ArrayCache.
How can I help you explore Laravel packages today?