An asynchronous caching abstraction layer for PHP with built-in rate limiting and stale-while-revalidate support. This library is designed to wrap promise-based operations (like Guzzle Promises) to provide robust caching strategies suitable for high-load or rate-limited API clients.
- Asynchronous Caching: Wraps
PromiseInterfaceor any callable returning a value/promise to handle caching transparently without blocking execution. - Stale-While-Revalidate: Supports background revalidation and stale-on-error patterns.
- X-Fetch (Probabilistic Early Recomputation): Implements the X-Fetch algorithm to prevent cache stampedes (dog-pile effect).
- Atomic Operations: Support for atomic
incrementanddecrementoperations using Symfony Lock. - Logical vs. Physical TTL: Separates the "freshness" of data from its "existence" in the cache, enabling soft expiration patterns.
- Rate Limiting Integration: Supports Symfony Rate Limiter for request throttling.
- PSR-16 & ReactPHP Compatible: Works with any PSR-16 Simple Cache adapter or ReactPHP Cache implementation.
To install the Async Cache PHP library, run the following command in your terminal:
composer require fyennyi/async-cache-phpThe easiest way to create a manager is using the AsyncCacheBuilder.
use Fyennyi\AsyncCache\AsyncCacheBuilder;
use Fyennyi\AsyncCache\Storage\ReactCacheAdapter;
use React\Cache\ArrayCache;
// 1. Setup Cache (using ReactPHP ArrayCache as an example)
$cacheAdapter = new ReactCacheAdapter(new ArrayCache());
// 2. Create the Manager using the Builder
$manager = AsyncCacheBuilder::create($cacheAdapter)
->build();Use the wrap method to cache a promise-based operation.
use Fyennyi\AsyncCache\CacheOptions;
use Fyennyi\AsyncCache\Enum\CacheStrategy;
use GuzzleHttp\Client;
$client = new Client();
$options = new CacheOptions(
ttl: 60, // Data is fresh for 60 seconds
strategy: CacheStrategy::Strict // Default strategy
);
$promise = $manager->wrap(
'cache_key_user_1',
fn() => $client->getAsync('https://api.example.com/users/1'),
$options
);
// Wait for the result (non-blocking if used within ReactPHP event loop)
$response = $promise->wait();The CacheOptions DTO allows you to configure behavior per request:
use Fyennyi\AsyncCache\Enum\CacheStrategy;
new CacheOptions(
ttl: 300, // Time in seconds data is considered fresh
stale_grace_period: 86400, // Keep stale data physically in cache for 24h
strategy: CacheStrategy::Strict, // Strict, Background, or ForceRefresh
rate_limit_key: 'nominatim', // Key for rate limiting (if limiter is configured)
serve_stale_if_limited: true, // Return stale data if rate limited
tags: ['geo', 'kyiv'], // Cache tags (if adapter supports them)
compression: false, // Enable data compression
x_fetch_beta: 1.0 // Beta coefficient for X-Fetch (0 to disable)
);$newValue = $manager->increment('page_views', 1)->wait();- Cache Hit: If data is found in the cache and is fresh (within
ttl), the promise resolves immediately with the cached value. The factory function is not called. - Cache Miss: If data is not found, the factory function is executed, and the result is stored in the cache.
- Stale Data:
- If data is in the cache but expired (older than
ttl), the manager behavior depends on the chosenstrategy. - Strict: Fetches fresh data while the request waits.
- Background: Returns stale data immediately and triggers an asynchronous refresh in the background.
- If data is in the cache but expired (older than
- X-Fetch: Helps avoid simultaneous cache misses for the same key by probabilistic early recomputation.
Contributions are welcome! Please follow these steps:
- Fork the project.
- Create your feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Push to the branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
This library is licensed under the CSSM Unlimited License v2.0 (CSSM-ULv2). See the LICENSE file for details.