bitandblack / request-cache
Caching of HTTP requested data with zero-latency reads: expired data is refreshed by background PHP processes, so your script never waits on the network. Works with any PSR-18/PSR-17 HTTP implementation via HTTP Discovery.
Requires
- php: >=7.4
- ext-json: *
- bitandblack/composer-helper: ^1.0 || ^2.0
- bitandblack/duration: ^1.0
- bitandblack/pathinfo: ^1.0 || ^2.0
- fig/http-message-util: ^1.0
- guzzlehttp/psr7: ^2.0 || ^3.0
- laravel/serializable-closure: ^1.1 || ^2.0
- php-http/discovery: ^1.0
- psr/http-client: ^1.0
- psr/http-client-implementation: *
- psr/http-factory: ^1.0
- psr/http-factory-implementation: *
- psr/log: ^1.0 || ^2.0 || ^3.0
- psr/simple-cache: ^1.0 || ^2.0
- symfony/finder: ^5.0 || ^6.0 || ^7.0 || ^8.0
Requires (Dev)
- bitandblack/helpers: ^1.8 || ^2.0
- monolog/monolog: ^2.0 || ^3.0
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^9.0
- rector/rector: ^2.0
- symfony/http-client: ^6.0 || ^7.0
- symfony/process: ^6.0 || ^7.0 || ^8.0
- symfony/var-dumper: ^7.0 || ^8.0
- symplify/easy-coding-standard: ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Bit&Black Request Cache
Caching of HTTP requested data, done right: smooth, non-blocking and with zero-latency reads.
The response is never requested inside your script. Instead, this library hands the HTTP request to a separate background PHP process and returns immediately. Your code never wait for the network.
Key benefits
- Zero-latency reads. A cache hit returns instantly. An expired entry still returns the last stored response while a fresh request upgrades it in the background (stale-while-revalidate). An unknown URL returns your default value immediately and fetches the data in the background, so the next call is already fast.
- Never blocks your script. The actual HTTP request is executed by a detached PHP CLI process that keeps working after your PHP-FPM/Apache request has already finished and the response has been sent. This is impossible with single-process "async" HTTP clients, which keep your worker busy until the network has answered.
- Pluggable HTTP layer. No hard dependency on a specific HTTP client. The library discovers an installed PSR-18 client and PSR-17 factories via php-http/discovery. Any implementation — Guzzle, Symfony HttpClient, and so on — works out of the box, both as the real client and as the PSR-7 factory.
- Simple API. Each request carries its own time-to-live (TTL) that decides when a refresh is needed.
RequestHandlerimplementsPsr\Http\Client\ClientInterface, so it can be dropped in wherever a PSR-18 client is expected.
Why not just an async HTTP client?
Async HTTP clients like symfony/http-client or guzzlehttp/promises solve a different problem: concurrency within a single process. They multiplex many sockets on an event loop, but the current PHP process stays busy until those requests finish, and they know nothing about caching or expiry.
This library optimizes for a different scenario — serving cached data with zero latency, even when the underlying source is slow or unavailable:
- Visitors always get an instant response; the slow network call happens off to the side.
- Background processes survive the request lifecycle, so a long-running request never keeps your users waiting.
- Caching and refresh are built in: one TTL value decides when to re-request, nothing else to configure.
If you need to fetch many URLs concurrently within a single script, an async HTTP client remains the right tool — and Request Cache can even use one as its underlying client via a custom request callback (see below).
Installation
This library is available for the use with Composer. Add it to your project by running $ composer require bitandblack/request-cache.
Usage
At first initialize a cache type and a process object, and use them to initialize the request handler object. In this example we're going to use the FileSystemCache:
<?php
use BitAndBlack\RequestCache\CacheType\FileSystemCache;
use BitAndBlack\RequestCache\Process;
use BitAndBlack\RequestCache\RequestHandler;
$cache = new FileSystemCache(__DIR__);
$process = new Process();
$requestHandler = new RequestHandler($cache, $process);
The request handler is now ready to be used. Now create a request object with the URL you want to request and the time to live (ttl). This example requests the URL https://www.bitandblack.com and allows the data to be stored for 1 hour:
<?php
use BitAndBlack\RequestCache\Request;
$request = new Request(
'https://www.bitandblack.com',
3600
);
The data can be requested now:
<?php
$response = $requestHandler->getResponse($request);
The request handler will return the response immediately and without blocking the script:
- If the requested URL has been cached and is still valid, the cached response is returned.
- If it has been cached but is expired, the last response is returned while the new data is requested in the background (stale-while-revalidate).
- If nothing has been cached yet, your default value is returned right away and the data is requested in the background, so the next call is fast.
When successful, the response will be a Psr\Http\Message\ResponseInterface object. The concrete implementation is provided by your PSR-17 response factory (see below).
If you want to block until the network answer arrives anyway — for example to fill the cache during a warm-up — pass true as the third argument: $requestHandler->getResponse($request, null, true). The PSR-18 compatible sendRequest() method does the same.
HTTP implementation
This library does not depend on a specific HTTP client. Instead, it uses php-http/discovery to find an installed PSR-18 client and PSR-17 factories at runtime. Any package that provides a psr/http-client-implementation or a psr/http-factory-implementation works out of the box, for example:
$ composer require guzzlehttp/guzzle
# or
$ composer require symfony/http-client nyholm/psr7
Requirements
The background request is executed by a separate PHP CLI process, so the following must be available in your environment:
- A
phpCLI binary (the path can be configured when instantiating theProcessobject). - The
exec()function (andpopen()on Windows) must not be disabled in yourphp.ini. - The background process must be able to load Composer's
vendor/autoload.php.
Options
Custom request client
Per default, the request client and the request/stream factories are resolved via HTTP Discovery. You can use the request callback to set up a custom request client or to modify the configuration.
<?php
use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;
$cache->setRequestCallback(
function (string $url): ResponseInterface
{
$client = new Client();
return $client->request(
'GET',
$url,
[
'allow_redirects' => false
]
);
}
);
TTL
The ttl value may be easier to set with the help of the bitandblack/duration library. For example instead of writing 86400 for the duration of a whole day, you can write Duration::createFromDays(1)->getSeconds() then.
Help
If you have any questions, feel free to contact us under hello@bitandblack.com.
Further information about Bit&Black can be found under www.bitandblack.com.