-
Notifications
You must be signed in to change notification settings - Fork 2
Cache
Pair v4 provides a small cache abstraction for framework metadata and application-level cache needs.
The default store is file-backed and dependency-free. APCu and Redis stores are available when the application explicitly configures them.
-
Pair\Cache\Cache: resolves the application-wide cache store for the current PHP process. -
Pair\Cache\CacheStore: small interface implemented by cache drivers. -
Pair\Cache\FileCacheStore: dependency-free default backed by files underTEMP_PATH/cache. -
Pair\Cache\ApcuCacheStore: optional APCu-backed store for single-node deployments. -
Pair\Cache\RedisCacheStore: optional Redis-backed store for shared production deployments.
Every store implements:
interface CacheStore {
public function get(string $key, mixed $default = null): mixed;
public function set(string $key, mixed $value, ?int $ttlSeconds = null): bool;
public function has(string $key): bool;
public function delete(string $key): bool;
public function clear(): bool;
}null TTL means no expiration.
A non-positive TTL removes the key immediately.
Values should be serializable by the selected backend.
use Pair\Cache\Cache;
Cache::store()->set('dashboard.summary', $summary, 300);
$summary = Cache::store()->get('dashboard.summary', []);Without configuration, Cache::store() returns a FileCacheStore under TEMP_PATH/cache.
The default store can also be configured through .env:
PAIR_CACHE_DRIVER=file
PAIR_CACHE_PATH=
PAIR_CACHE_PREFIX=pairSupported drivers:
-
file: dependency-free default. UsesPAIR_CACHE_PATHwhen set, otherwiseTEMP_PATH/cache. -
apcu: uses APCu when the extension is installed and enabled. -
redis: uses Redis through theRedisPHP extension and the sharedREDIS_*settings.
PAIR_CACHE_PREFIX namespaces file and APCu keys. Empty prefixes fall back to pair.
For normal module or API code, use the static helpers:
use Pair\Cache\Cache;
Cache::set('dashboard.summary', $summary, 300);
$summary = Cache::get('dashboard.summary', []);
$summary = Cache::remember('dashboard.summary', function () {
return buildDashboardSummary();
}, 300);Available helpers:
Cache::get(string $key, mixed $default = null): mixedCache::set(string $key, mixed $value, ?int $ttlSeconds = null): boolCache::has(string $key): boolCache::delete(string $key): boolCache::clear(): boolCache::remember(string $key, callable $resolver, ?int $ttlSeconds = null): mixed
remember() is a convenience helper for ordinary cache-aside reads. It is not a distributed lock; under high concurrency the resolver may run more than once.
When Observability is enabled, the static helpers emit cache spans such as cache.get, cache.set, and cache.remember. Span attributes include a hash of the cache key, not the raw key.
use Pair\Cache\Cache;
use Pair\Cache\FileCacheStore;
Cache::setStore(new FileCacheStore(TEMP_PATH . 'cache', 'app'));Tests and long-running commands can reset the configured store:
Cache::clearStore();use Pair\Cache\ApcuCacheStore;
use Pair\Cache\Cache;
if (ApcuCacheStore::isAvailable()) {
Cache::setStore(new ApcuCacheStore('pair'));
}APCu is local to the PHP runtime. It is useful on a single node, but not for shared cache across multiple servers.
APCu can be selected through .env:
PAIR_CACHE_DRIVER=apcu
PAIR_CACHE_PREFIX=pairuse Pair\Cache\Cache;
use Pair\Cache\RedisCacheStore;
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
Cache::setStore(new RedisCacheStore($redis, 'pair:cache:'));The Redis store wraps an existing Redis-compatible client. Pair does not require Redis in the core runtime.
Redis can be selected through .env:
PAIR_CACHE_DRIVER=redis
PAIR_CACHE_REDIS_PREFIX="pair:cache:"
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_TIMEOUT=1PAIR_CACHE_REDIS_PREFIX namespaces Redis keys independently from the API rate limiter prefix.
Pair\Api\Idempotency uses CacheStore and defaults to a file-backed store under TEMP_PATH/idempotency.
use Pair\Api\Idempotency;
use Pair\Cache\RedisCacheStore;
Idempotency::setStore(new RedisCacheStore($redis, 'pair:idempotency:'));RateLimiter still has specialized atomic file/Redis logic because throttling needs sliding-window operations that are stricter than the generic cache contract.
- Use clear, namespaced keys such as
module:resource:id. - Do not store secrets unless the selected backend is protected appropriately.
- Use TTLs for data that can become stale.
- Keep comments and docblocks explanatory only; cache behavior belongs in code and configuration.
See also: Env, Observability, FilesystemMetadata, CrudResourceMetadata, RateLimiter, Idempotency.