Useful work, remembered.
PSR-16 cache and fixed-window rate limits, with file or optional Redis backends.
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. Choose the optional Redis backend below when multiple processes or nodes need one limit. 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.
Optional Redis backend
Predis is installed as a development dependency for integration tests. Promote it to a runtime requirement if you select Redis in production. Both PSR-16 cache and API/login limits switch together; the default remains file. Configure a private Redis endpoint and a unique namespace, with transport/ACL controls appropriate to your infrastructure.
composer require 'predis/predis:^3.0'
# Private environment configuration:
CACHE_DRIVER=redis
CACHE_NAMESPACE=my_application
REDIS_URL=tcp://127.0.0.1:6379
# Run actual Redis tests against an isolated instance:
ICOM_TEST_REDIS_URL=tcp://127.0.0.1:6379 composer testRedis consistency and failures
The limiter performs one atomic Lua operation using Redis server time and key expiry. A Redis connection failure raises an error; it never silently allows unlimited traffic. Cache values carry a namespace/key-bound HMAC and native TTL. clear() scans and deletes only its cache namespace, never FLUSHDB or rate keys. The adapter targets one logical Redis primary; cluster-wide SCAN and a transactional clear during concurrent writes are not supported. Configure Redis persistence, failover and eviction policies separately. A limiter database that evicts live counters can weaken limits.