HTTP Caching
Caching is opt-in: attach a CacheManager to a handler with withCache(). A request's cache option controls a manager that is already attached; it does not create one by itself.
use Fetch\Cache\FileCache;
$handler = fetch_client()
->getHandler()
->withCache(new FileCache('/tmp/fetch-cache'));
$response = $handler->get('https://api.example.test/v1/catalog');The default cache policy targets GET and HEAD and a defined set of cacheable statuses, with a default TTL of 3600 seconds. HTTP Cache-Control, validators, and selected Vary request headers influence storage and revalidation. Private caching is disabled by default. Review CacheManager options before persisting data that may contain credentials or user-specific responses.
Cache lookup and storage use the final middleware-transformed request options. Authorization and Cookie headers always partition cache entries, including custom cache keys. Basic-auth options and URL-matched cookie jars are converted to those headers before key generation. Credential values are hashed rather than written into generated keys. A custom key still controls URL/resource variation, so never reuse one key for unrelated resources. Application identity carried outside these headers needs an explicit varying header or custom key.
The async and streaming request paths do not currently go through the cache manager. MockServer returns fakes outside the cache pipeline; use an injected Guzzle MockHandler when testing cache hits and revalidation.
<?php
declare(strict_types=1);
require dirname(__DIR__, 3).'/vendor/autoload.php';
use Fetch\Cache\MemoryCache;
use Fetch\Enum\Method;
use Fetch\Http\ClientHandler;
use Fetch\Interfaces\Response as FetchResponse;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
$mock = new MockHandler([
new GuzzleResponse(
200,
['Content-Type' => 'application/json'],
'{"version":1}',
),
]);
$client = new GuzzleClient(['handler' => HandlerStack::create($mock)]);
$handler = ClientHandler::createWithClient($client)->withCache(
new MemoryCache,
['default_ttl' => 60],
);
$first = $handler->sendRequest(
Method::GET,
'https://api.example.test/v1/catalog',
);
$second = $handler->sendRequest(
Method::GET,
'https://api.example.test/v1/catalog',
);
if (
! ($first instanceof FetchResponse)
|| ! ($second instanceof FetchResponse)
) {
throw new RuntimeException('The cache requests did not return responses.');
}
if (
! $second->hasHeader('X-Cache-Status') ||
$second->getHeaderLine('X-Cache-Status') !== 'HIT'
) {
throw new RuntimeException('The cache did not serve the second response.');
}