Cache Facade (Cache)#
Infocyph\CacheLayer\Cache\Cache is the unified facade for CacheLayer.
It implements:
PSR-6 (
CacheItemPoolInterface)PSR-16 (
Psr\SimpleCache\CacheInterface)ArrayAccessCountable
It also adds tagged invalidation, stampede-safe remember(), lock provider
selection, metrics hooks, and payload compression controls.
CacheLayer was separated from the existing Intermix project for better standalone visibility and faster cache-specific feature enrichment.
Installation#
composer require infocyph/cachelayer
Quick Example#
use Infocyph\CacheLayer\Cache\Cache;
$cache = Cache::file('app', __DIR__ . '/storage/cache');
$user = $cache->remember('user:42', function ($item) {
$item->expiresAfter(300);
return fetchUserFromDatabase(42);
}, tags: ['users']);
$cache->invalidateTag('users');
Factory Methods#
The facade exposes factory methods for all bundled adapters:
Cache::local(string $namespace = 'default', ?string $dir = null)Cache::file(string $namespace = 'default', ?string $dir = null)Cache::phpFiles(string $namespace = 'default', ?string $dir = null)Cache::apcu(string $namespace = 'default')Cache::memcache(string $namespace = 'default', array $servers = [['127.0.0.1', 11211, 0]], ?Memcached $client = null)Cache::redis(string $namespace = 'default', string $dsn = 'redis://127.0.0.1:6379', ?Redis $client = null)Cache::valkey(string $namespace = 'default', string $dsn = 'valkey://127.0.0.1:6379', ?Redis $client = null)Cache::redisCluster(string $namespace = 'default', array $seeds = ['127.0.0.1:6379'], float $timeout = 1.0, float $readTimeout = 1.0, bool $persistent = false, ?object $client = null)Cache::sqlite(string $namespace = 'default', ?string $file = null)Cache::pdo(string $namespace = 'default', ?string $dsn = null, ?string $username = null, ?string $password = null, ?PDO $pdo = null, string $table = 'cachelayer_entries')Cache::memory(string $namespace = 'default')Cache::weakMap(string $namespace = 'default')Cache::sharedMemory(string $namespace = 'default', int $segmentSize = 16777216)Cache::nullStore()Cache::chain(array $pools)Cache::tiered(array $tiers, bool $writeToL1 = true)Cache::mongodb(string $namespace = 'default', ?object $collection = null, ?object $client = null, string $database = 'cachelayer', string $collectionName = 'entries', string $uri = 'mongodb://127.0.0.1:27017')Cache::scyllaDb(string $namespace = 'default', ?object $session = null, string $keyspace = 'cachelayer', string $table = 'cachelayer_entries')
local() chooses APCu when available (extension_loaded('apcu') and apcu_enabled()), otherwise File cache.
pdo() defaults to SQLite (temp-file database per namespace) when DSN/PDO is not provided.
sqlite() is a convenience wrapper over pdo() for explicit SQLite file selection.
tiered() accepts either concrete pool instances or descriptor arrays with a
driver key (for example apcu, valkey, redis, pdo, sqlite).
Use writeToL1 = false to skip write-through to the first tier while still
allowing promotion from lower tiers on read.
Tiered L1/L2/DB flow example:
use Infocyph\CacheLayer\Cache\Cache;
$cache = Cache::tiered([
['driver' => 'apcu', 'namespace' => 'app'], // L1
['driver' => 'valkey', 'namespace' => 'app', 'dsn' => 'valkey://127.0.0.1:6379'], // L2
], writeToL1: false); // optional L1 write-through
$value = $cache->remember('user:42', function ($item) use ($pdo) {
$item->expiresAfter(300);
$stmt = $pdo->prepare('SELECT payload FROM users_cache_source WHERE id = ?');
$stmt->execute([42]);
return $stmt->fetchColumn();
});
Request path:
check APCu (L1)
check Redis/Valkey (L2)
query DB on miss
write L2
optionally write L1 (
writeToL1)
Key and TTL Rules#
Key validation is strict and shared across PSR-6/PSR-16 calls:
Allowed characters:
A-Z,a-z,0-9,_,.,-Empty keys or keys with spaces are rejected
Invalid keys throw
Infocyph\CacheLayer\Exceptions\CacheInvalidArgumentException
TTL handling:
Supported types:
null,int,DateIntervalNegative TTL is rejected
TTL
0behaves as immediate expiry (adapters treat it as delete/expired)
PSR-16 Methods#
Common helpers:
get(string $key, mixed $default = null): mixedset(string $key, mixed $value, int|DateInterval|null $ttl = null): booldelete(string $key): boolclear(): boolgetMultiple(iterable $keys, mixed $default = null): iterablesetMultiple(iterable $values, int|DateInterval|null $ttl = null): booldeleteMultiple(iterable $keys): boolhas(string $key): bool
get() callable default#
If $default is callable, get() internally uses remember() semantics.
On miss, the callable is executed and the result is persisted.
$value = $cache->get('profile:42', function ($item) {
$item->expiresAfter(120);
return computeProfile();
});
PSR-6 Methods#
Standard pool methods are available and delegated to the underlying adapter:
getItem()getItems()hasItem()save()saveDeferred()commit()deleteItem()deleteItems()clear()
For adapters that implement multiFetch(array $keys), getItems() uses it
for efficient batch retrieval.
Tagged Caching#
CacheLayer uses tag-version invalidation (no full key scans required):
setTagged(string $key, mixed $value, array $tags, mixed $ttl = null): boolinvalidateTag(string $tag): boolinvalidateTags(array $tags): bool
When a tag is invalidated, its internal version is incremented. Entries tagged with older versions become stale and are treated as misses on read.
$cache->setTagged('home:feed', $payload, ['feed', 'home'], 300);
$cache->invalidateTag('feed');
$cache->get('home:feed'); // null (stale)
Stampede-Safe remember()#
remember() protects expensive recomputation with a lock provider:
$value = $cache->remember('report:daily', function ($item) {
$item->expiresAfter(60);
return buildDailyReport();
}, tags: ['reports']);
Behavior:
Read existing value.
On miss, acquire lock (
FileLockProviderby default).Re-check value under lock.
Compute and save value.
Apply jitter to TTL to reduce herd effects.
Release lock.
Lock provider selection:
setLockProvider(LockProviderInterface $provider): selfuseRedisLock(?Redis $client = null, string $prefix = 'cachelayer:lock:'): selfuseValkeyLock(?Redis $client = null, string $prefix = 'cachelayer:lock:'): selfuseMemcachedLock(?Memcached $client = null, string $prefix = 'cachelayer:lock:'): self
Factory defaults:
Cache::redis(...)auto-configuresRedisLockProviderCache::valkey(...)auto-configuresRedisLockProviderCache::memcache(...)auto-configuresMemcachedLockProviderCache::pdo(...)/Cache::sqlite(...)auto-configurePdoLockProviderother adapters default to
FileLockProvider
Metrics and Export Hooks#
Methods:
setMetricsCollector(CacheMetricsCollectorInterface $metrics): selfexportMetrics(): arraysetMetricsExportHook(?callable $hook): self
Default collector is InMemoryCacheMetricsCollector.
Metrics are grouped by readable adapter name and metric name, for example:
[
'file' => [
'hit' => 10,
'miss' => 4,
'set' => 3,
],
]
Payload Compression#
Use configurePayloadCompression(?int $thresholdBytes = null, int $level = 6)
to enable compression for encoded payloads.
Notes:
Compression is applied when payload size meets/exceeds threshold.
Requires
gzencode/gzdecodefunctions.Compression configuration is global (
CachePayloadCodecstatic state).
Payload and Serialization Security#
Methods:
configurePayloadSecurity(?string $integrityKey = null, ?int $maxPayloadBytes = 8388608): selfconfigureSerializationSecurity(bool $allowClosurePayloads = true, bool $allowObjectPayloads = true): self
Example:
$cache
->configurePayloadSecurity(
integrityKey: 'replace-with-strong-secret',
maxPayloadBytes: 8_388_608,
)
->configureSerializationSecurity(
allowClosurePayloads: false,
allowObjectPayloads: false,
);
Environment variables:
CACHELAYER_PAYLOAD_INTEGRITY_KEYCACHELAYER_MAX_PAYLOAD_BYTES
Convenience Features#
Array and magic access:
$cache['key'] = 'value';$cache['key'];$cache->key = 'value';$cache->key;
Counting:
count($cache)delegates to adapterCountablesupport when available.
Namespace/Directory Mutation#
setNamespaceAndDirectory(string $namespace, ?string $dir = null): void
forwards to adapters that support runtime namespace/directory changes.
Supported by:
File cache adapter
PHP files cache adapter
Unsupported adapters throw BadMethodCallException.