Cache - viames/pair GitHub Wiki
Pair framework: 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.
Main classes
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.
CacheStore contract
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.
Default file store
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=pair
Supported 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.
Application-level usage
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.
Configure a custom store
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();
APCu store
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=pair
Redis store
use 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=1
PAIR_CACHE_REDIS_PREFIX namespaces Redis keys independently from the API rate limiter prefix.
Framework consumers
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.
Best practices
- 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.