rekalogika/psr-16-simple-cache-bundle
A Symfony bundle for integrating PSR-16 simple cache implementations into your app, with service configuration and autowiring so you can use interoperable, lightweight caching through the framework’s dependency injection.
The package is a Symfony bundle, not a Laravel package — rekalogika/psr-16-simple-cache-bundle exists to wire PSR-16 Psr\SimpleCache\CacheInterface services on top of Symfony cache pools so you can inject a framework-agnostic cache into code that typehints the PSR-16 interface.
Minimal steps (Symfony app):
composer require rekalogika/psr-16-simple-cache-bundle — as of 1.2.0 this supports PHP 8.4 alongside the earlier 8.x line.config/bundles.php:
return [
// ...
Rekalogika\Psr16SimpleCacheBundle\RekalogikaPsr16SimpleCacheBundle::class => ['all' => true],
];
framework.cache.pools) and point the bundle's config at it, or bind the resulting service ID to Psr\SimpleCache\CacheInterface for autowiring.Psr\SimpleCache\CacheInterface into one service and verify with bin/console debug:container 'Psr\SimpleCache\CacheInterface'.Where to look first: the bundle's config tree (bin/console config:dump-reference rekalogika_psr16_simple_cache — confirm the root key against the README) and the service definitions that expose CacheInterface. The README is short; the DI config is the real documentation.
First use case: a third-party library that accepts a PSR-16 cache in its constructor (HTTP client cache, rate limiter, template fragment cache, feature-flag client). Instead of hand-rolling new Psr16Cache(new FilesystemAdapter(...)) in a service factory, you let the bundle provide the service and inject it.
Single default cache, autowired. Define one cache pool, expose it through the bundle, and alias it to Psr\SimpleCache\CacheInterface. Every consumer then gets the same namespace with zero boilerplate — good default for small apps.
Named caches per concern. In real apps you want isolation: app.psr16.rate_limit, app.psr16.http, app.psr16.feature_flags. Give each its own framework.cache.pools entry (each pool gets its own namespace/prefix), and inject by named service or by binding a specific CacheInterface $rateLimitCache argument in services.yaml. Never share one PSR-16 service across concerns — clear() on one consumer wipes everything.
Adapter bridge for third-party code. The common integration shape is: your app code uses Symfony contracts; the boundary library uses PSR-16. Keep the PSR-16 service at the boundary only, and don't let CacheInterface leak into domain services.
final class WeatherClient
{
public function __construct(private CacheInterface $cache) {}
public function forecast(string $city): array
{
$key = 'weather.'.md5($city);
if ($this->cache->has($key)) {
return $this->cache->get($key);
}
$data = $this->fetch($city);
$this->cache->set($key, $data, 900);
return $data;
}
}
TTL policy in config, not in call sites. Set a sensible default_lifetime on the pool, pass explicit TTLs only where the domain demands it. Remember PSR-16 accepts int|DateInterval|null.
Testing. Override the service in config/packages/test/ with a Psr16Cache(new ArrayAdapter()) (or a NullAdapter) so tests never hit Redis/filesystem. Because you injected an interface, no production code changes.
Operational hooks. bin/console cache:pool:clear <pool> and cache:clear are your levers; the bundle adds no separate invalidation command.
config/cache.php integration, and Laravel's auto-discovery won't pick it up. Laravel's Illuminate\Contracts\Cache\Repository looks PSR-16-ish (get/put/remember) but does not implement Psr\SimpleCache\CacheInterface. On Laravel, skip the bundle and use symfony/cache's Psr16Cache over an adapter, wrapped in your own singleton binding — or an existing Laravel PSR-16 bridge.{ } ( ) / \ @ : in keys; libraries that build keys from URLs or FQCNs throw InvalidArgumentException at runtime, not at boot. Hashing user input into the key is the cheap fix.null is ambiguous. get($key, $default) cannot distinguish a stored null from a miss. Store a sentinel or use has() first — but be aware has() + get() is two round trips.ttl = 0 meaning "forever".clear() is namespace-scoped and destructive. It nukes the whole pool namespace, including entries written by other code sharing that pool. One pool per logical cache.^1.2 bump from 1.1.0. Watch sibling Symfony packages for implicit-nullable deprecation notices, not this bundle.framework being registered with a cache configured; in multi-app kernels make sure the config loads after framework.yaml.bin/console debug:container --tag=cache.pool, bin/console debug:container 'Psr\SimpleCache\CacheInterface' -vv, and debug:autowiring cache. Most "cannot autowire" errors here are a missing alias, not a bundle bug.CacheInterface service for logging/metrics (key hashing, hit/miss counters), or define your own service plus an alias rather than fighting the bundle's config. The bundle is thin enough that a decorator is usually less work than a new config option.How can I help you explore Laravel packages today?