roolith / cache
PSR-6 and PSR-16 compatible cache system using PHP
Requires
- php: ^8.0
- nesbot/carbon: ^2.0 || ^3.0
- psr/cache: ^1.0 || ^2.0 || ^3.0
- psr/simple-cache: ^1.0 || ^2.0 || ^3.0
Requires (Dev)
- phpunit/phpunit: ^9.2
Suggests
None
Provides
- psr/cache-implementation: ^1.0 || ^2.0 || ^3.0
- psr/simple-cache-implementation: ^1.0 || ^2.0 || ^3.0
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-04 05:14:17 UTC
README
PSR-6 and PSR-16 compatible cache system using PHP. Only the file driver is available. Any unknown driver name falls back to the file driver.
Install
composer require roolith/cache
Requirements
- PHP ^8.0.
psr/cache^1.0 or ^2.0 or ^3.0.psr/simple-cache^1.0 or ^2.0 or ^3.0.nesbot/carbon^2.0 or ^3.0.
Bootstrap cache directory
Define ROOLITH_CACHE_DIR before vendor/autoload.php is loaded and before the first CacheFactory call.
This order keeps the constant visible to the factory resolver at runtime.
If you cannot define a constant early, set CacheFactory::$fileDriverCacheDir before use or pass an explicit ['dir' => ...] config.
When no dir is configured, the factory falls back to sys_get_temp_dir() . '/roolith-cache'.
FileDriver itself is strict.
Calling bootstrap() without a non-empty string dir throws InvalidArgumentException.
<?php define('ROOLITH_CACHE_DIR', __DIR__ . '/cache'); require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Cache\CacheFactory; CacheFactory::put('a', 'b', 3600); echo CacheFactory::get('a');
Explicit config always wins over the constant and over the static override. The static override wins over the constant and over the temp fallback.
<?php require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Cache\CacheFactory; // Highest priority: explicit dir. CacheFactory::driver('file', ['dir' => __DIR__ . '/cache']); // Middle priority: static override. CacheFactory::$fileDriverCacheDir = __DIR__ . '/cache'; // Lowest priority: ROOLITH_CACHE_DIR constant or temp fallback.
Usage
You may choose any method.
All dir values must be non-empty strings, otherwise FileDriver::bootstrap() throws InvalidArgumentException.
Method 1: Factory
<?php define('ROOLITH_CACHE_DIR', __DIR__ . '/cache'); require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Cache\CacheFactory; // Will save cache with 1-hour TTL. CacheFactory::put('a', 'b', 3600); // Will retrieve cache or false when missing, expired, or corrupt. CacheFactory::get('a'); // You can select driver and store. CacheFactory::driver('file')->put('a', 'b', 3600); // Will return boolean. CacheFactory::has('foo'); // Will delete cache item, false when the key has no valid entry. CacheFactory::remove('foo'); // Will delete all `*.rcache` items in the configured dir. CacheFactory::flush();
Method 2: Cache
<?php require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Cache\Cache; $cache = new Cache(); $cache->driver('file', ['dir' => __DIR__ . '/cache']); // Third argument is seconds and defaults to 3600. $cache->put('foo', 'bar', 3600); print_r($cache->get('foo'));
Method 3: PSR-6
<?php require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Driver\FileDriver; use Roolith\Caching\Cache\Pool; $fileDriver = new FileDriver(['dir' => __DIR__ . '/cache']); $pool = new Pool($fileDriver); $item = $pool->getItem('foo'); if (!$item->isHit()) { $item->set([1, 2, 3])->expiresAfter(3600); $pool->save($item); } print_r($item->get());
Pool::getItems() preserves input keys in the returned array.
Pool::save() without an explicit expiration falls back to the item default, which is one month from creation.
Method 4: PSR-16
<?php require __DIR__ . '/vendor/autoload.php'; use Roolith\Caching\Cache\SimpleCache; use Roolith\Caching\Driver\FileDriver; $fileDriver = new FileDriver(['dir' => __DIR__ . '/cache']); $simpleCache = new SimpleCache($fileDriver); $simpleCache->set('foo', 'bar', 3600); print_r($simpleCache->get('foo'));
SimpleCache::getMultiple() preserves input keys in the returned array.
Bulk methods accept arrays or Traversable and validate every key.
Invalid keys throw PSR-16 InvalidArgumentException.
TTL semantics
Cache::put($key, $value, $expireAfter = 3600) takes seconds and defaults to one hour.
Item::expiresAfter() accepts an integer in seconds, a DateInterval as a total duration via Carbon::now()->add(), or null for the item default.
Item::expiresAt() accepts a DateTimeInterface or null for the item default.
Item defaults to one month from construction, and Pool::save() re-applies that default when expiration is null.
SimpleCache::set() uses a 5-hour default when TTL is null (SimpleCache::DEFAULT_TTL_HOURS).
An integer TTL is seconds, a DateInterval TTL is added as a total duration, and zero or negative integers expire immediately.
Any other TTL type such as string, float, bool, array, or object throws PSR-16 InvalidArgumentException.
setMultiple() validates TTL before writing so an invalid TTL fails without partial writes.
<?php use Roolith\Caching\Cache\SimpleCache; use Roolith\Caching\Driver\FileDriver; $cache = new SimpleCache(new FileDriver(['dir' => __DIR__ . '/cache'])); $cache->set('null-ttl', 'v', null); // Expires in 5 hours. $cache->set('seconds', 'v', 3600); // Expires in 1 hour. $cache->set('interval', 'v', new DateInterval('P1D')); // Expires in 1 full day. $cache->set('gone', 'v', 0); // Immediately expired.
Hits, falsy values, and expiration
Item::isHit() returns an explicit hit flag, not value truthiness.
Falsy values 0, false, null, [], and '' round-trip with isHit() === true while unexpired.
Missing, expired, corrupt, tampered, empty, or object-payload entries return isHit() === false for PSR-6 and false or $default for PSR-16 and FileDriver::get().
FileDriver::has() checks both payload validity and expiration.
FileDriver::get() checks validity before expiration and returns false for invalid or expired payloads.
There is no lingering falsy-value caveat.
If you see a miss for a falsy value, the entry is actually missing or expired.
Storage details
Keys are mapped to safe-prefix + '-' + sha1(full-key) + '.' + ext.
The prefix keeps the first 32 safe characters for readability, while the SHA1 prevents foo/bar versus foo-bar versus FOO-BAR collisions.
The ext config is whitelisted to alphanumeric only and falls back to rcache.
Payloads store expiration as a Unix timestamp and use unserialize() with allowed_classes => false.
Tampered, empty, object-payload, or bad-shape payloads fail validation and read as miss.
Writes are atomic via temp file plus LOCK_EX plus rename(), and reads use a shared lock.
flush() only deletes *.<ext> files, skips dot entries and directories, and checks is_file() before unlink().
Drivers
Only the file driver is implemented.
Cache::driver() switches on 'file' with a default branch, so unknown names currently fall back to FileDriver instead of throwing.
Pass ['dir' => $dir] and optionally ['ext' => $ext] when constructing FileDriver directly.
Development
./vendor/bin/phpunit --testdox tests