-
Notifications
You must be signed in to change notification settings - Fork 0
Recipes
Practical patterns built on the plain PSR-16 API. They work with any handler.
The cache-aside / read-through pattern, factored into one helper. It uses
has() so a producer that legitimately returns null is still cached:
use InitPHP\Cache\CacheInterface;
function remember(CacheInterface $cache, string $key, int $ttl, callable $producer): mixed
{
if ($cache->has($key)) {
return $cache->get($key);
}
$value = $producer();
$cache->set($key, $value, $ttl);
return $value;
}$settings = remember($cache, 'settings', 3600, function () use ($db) {
return $db->query('SELECT * FROM settings')->fetchAll();
});$key = 'fib_' . $n;
$result = remember($cache, $key, 86400, fn () => fibonacci($n));Build the key from whatever makes the query unique, and hash it to stay within the key rules:
function cachedQuery(CacheInterface $cache, PDO $db, string $sql, array $params, int $ttl = 300): array
{
$key = 'q_' . hash('xxh3', $sql . '|' . serialize($params));
return remember($cache, $key, $ttl, function () use ($db, $sql, $params) {
$stmt = $db->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
});
}$active = cachedQuery($cache, $db, 'SELECT * FROM users WHERE status = ?', ['active']);Remember to invalidate when the underlying data changes:
function updateUserStatus(CacheInterface $cache, PDO $db, int $id, string $status): void
{
$db->prepare('UPDATE users SET status = ? WHERE id = ?')->execute([$status, $id]);
$cache->delete('q_' . hash('xxh3', 'SELECT * FROM users WHERE status = ?|' . serialize(['active'])));
}Cache invalidation is the hard part. Keep keys derivable so you can delete the exact entries you wrote, or use short TTLs and accept brief staleness.
function fetchJson(CacheInterface $cache, string $url, int $ttl = 600): array
{
return remember($cache, 'http_' . hash('xxh3', $url), $ttl, function () use ($url) {
return json_decode(file_get_contents($url), true);
});
}function renderNav(CacheInterface $cache, callable $render): string
{
return remember($cache, 'frag_nav', 600, $render);
}
echo renderNav($cache, fn () => view('partials/nav', ['user' => $user]));Develop on the filesystem, run Redis in production — one place decides:
use InitPHP\Cache\Cache;
use InitPHP\Cache\Handler\File;
use InitPHP\Cache\Handler\Redis;
function makeCache(string $env): \InitPHP\Cache\CacheInterface
{
return match ($env) {
'production', 'staging' => Cache::create(Redis::class, [
'host' => getenv('REDIS_HOST') ?: '127.0.0.1',
'database' => 1,
'prefix' => 'app_',
]),
default => Cache::create(File::class, [
'path' => __DIR__ . '/var/cache',
'prefix' => 'app_',
]),
};
}
$cache = makeCache(getenv('APP_ENV') ?: 'local');A cache is cheap to build but you usually want a single configured instance.
Register it once and inject the CacheInterface:
// Pseudo-container registration
$container->singleton(CacheInterface::class, fn () => makeCache(getenv('APP_ENV')));
// Anywhere else — depend on the interface, not the handler
final class ReportService
{
public function __construct(private CacheInterface $cache) {}
public function daily(): array
{
return remember($this->cache, 'daily_report', 86400, fn () => $this->build());
}
}When a popular key expires, many concurrent requests can recompute it at once. Simple mitigations:
-
Jitter the TTL so keys don't all expire together:
$cache->set($key, $value, 3600 + random_int(0, 300));
- Refresh slightly early by storing your own "soft expiry" timestamp inside the value and recomputing in one request when it's near.
- For strict single-flight, guard the recompute with a short-lived lock key
(
set('lock_'.$key, 1, 5)and only the winner rebuilds).
For most apps, jittered TTLs are enough.
- Counters — view counters and soft rate limits.
- Testing — test code that depends on a cache.
- Error Handling — degrade gracefully when a backend is down.
initphp/cache · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Core Concepts
Handlers
Guides
Other