Useful work, remembered.
A local PSR-16 cache and a process-safe fixed-window limiter.
Use the cache interface
use Psr\SimpleCache\CacheInterface;
$app->get('/catalog', function (CacheInterface $cache) {
$cache->set('catalog-count', 12, 60);
return Response::json(['count' => $cache->get('catalog-count', 0)]);
});Integrity and expiration
Cache writes use temporary files and atomic rename. A per-installation key authenticates each filename and serialized payload before deserialization, rejecting altered records. Keys never become filesystem paths. Integer/DateInterval TTLs are supported; zero/negative TTL deletes. Null is a valid cached value. Keep storage private and never share APP_KEY with untrusted writers. PHP-serialized objects require their original classes to be available. Cache values are authenticated, not encrypted.
Rate limits
use Icom\Cache\RateLimiter;
$limiter = new RateLimiter($privateDirectory);
$state = $limiter->consume('customer-123', limit: 60, seconds: 60);
// allowed, remaining, retry_after, resetOperational scope
The demo allows 120 API requests per minute per directly connected IP and returns Retry-After on 429. Forwarded IP headers are ignored. A fixed window can burst at boundaries. File locks coordinate only processes sharing one local filesystem; this is not a distributed Redis limiter. Put cluster-wide rate limits at a trusted gateway. Rate files persist per key; monitor disk usage and clean offline during maintenance. Never delete active lock files. Expired cache files can be cleared with the CLI.