bakame / http-cache-status
Cache-Status HTTP Response Header Field in PHP
Fund package maintenance!
nyamsprod
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/bakame/http-cache-status
Requires
- php: ^8.2
- bakame/http-structured-fields: ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94.0
- guzzlehttp/psr7: ^2.8
- phpstan/phpstan: ^2.1.39
- phpstan/phpstan-deprecation-rules: ^2.0.4
- phpstan/phpstan-phpunit: ^2.0.15
- phpstan/phpstan-strict-rules: ^2.0.10
- phpunit/phpunit: ^11.5.53 || ^12.5.11 || ^13.0.2
- symfony/var-dumper: ^6.4.15 || ^7.4.4 || ^v8.0.4
README
This package contains classes used to parse, validate and manipulate the Cache-Status HTTP Response Header Field.
System Requirements
PHP >= 8.2 is required but the latest stable version of PHP is recommended.
Usage
Unless explicitly stated, all the classes described hereafter are immutable.
Parsing
The package can parse the Header from ab HTTP Request or Response using the Field class as follows:
use Bakame\Http\CacheStatus\Field; use Bakame\Http\CacheStatus\ForwardedReason; use Bakame\Http\CacheStatus\HandledRequestCache; /* @var Psr\Http\Message\ResponseInterface $response */ $headerLine = $response->getHeaderLine(Field::NAME); // 'ReverseProxyCache; hit, ForwardProxyCache; fwd=uri-miss; collapsed; stored'; $statusCode = $response->getStatusCode(); //304 $cacheStatus = Field::fromHttpValue($headerLine, $statusCode);
Field Container
The Field class is a container whose members are handled request cache information as they are added by the various
servers. The class implements PHP's IteratorAggregate, ArrayAccess, Countable and Stringable interface.
echo $cacheStatus; // returns 'ReverseProxyCache; hit, ForwardProxyCache; fwd=uri-miss; collapsed; stored'; $cacheStatus[1]; // returns a HandledRequestCache instance count($cacheStatus); // returns 2
You can also determine if a specific handled cache request exist either by supplying the cache index or its server identifier
$cacheStatus->contains(Token::fromString('foobar')); // returns false $cacheStatus->indexOf(Token::fromString('foobar')); // returns null $cacheStatus->contains(Token::fromString('ReverseProxyCache')); // returns true $cacheStatus->indexOf(Token::fromString('ReverseProxyCache')); // returns 0
As per the RFC the closestToOrigin and closestToUser methods give you access to the caches closest to the
origin server and the one closest to the client (user).
$cacheClosestToTheOrigin = $cacheStatus->closestToOrigin(); // the handled request cache closest to the origin server $cacheClosestToTheClient = $cacheStatus->closestToUser(); // the handled request cache closest to the user
Both methods return null if no HandledRequestCache instance is found.
The Handled Request Cache object
$cacheClosestToTheOrigin->hit; // return true $cacheClosestToTheOrigin->forward; // return null $cacheClosestToTheClient->hit; // return false $cacheClosestToTheClient->forward->reason; // return ForwardReason::UriMiss $cacheClosestToTheClient->forward->statusCode; // return 304
A HandledRequestCache instance contains information about the cache and how it was handled for the current message.
In particular, in compliance with the RFC, if the forward property is present you will get extra information
regarding the reason why the cache was forwarded. From the POV of the cache the only required check
needed is the following:
if ($cacheClosestToTheOrigin->hit) { //this is a hit, the 'forward' property is null $cacheClosestToTheOrigin->forward; // returns null } else { //not a hit, the 'forward' property is a Forward instance $cacheClosestToTheOrigin->forward; // return Forward class $cacheClosestToTheClient->forward->reason; // return ForwardReason::UriMiss $cacheClosestToTheClient->forward->statusCode; // return 304 if ($cacheClosestToTheClient->forwardReason->isOneOf(ForwardedReason::Miss, ForwardedReason::UriMiss)) { //you can do something useful here } }
The class lists all the available reason via the ForwardedReason Enum.
You can push more HandledRequestCache instances to the Field container using the push method.
The method supports pushing HandledRequestCache instances as well as HTTP text representation of the handled request
cache.
$newCacheStatus = $cacheStatus->push( HandledRequestCache::serverIdentifierAsToken('BrowserCache') ->wasForwarded(Forward::fromReason(ForwardedReason::UriMiss)) ); // or you can use push an HTTP header $newCacheStatus = $cacheStatus->push('BrowserCache; fwd=uri-miss');
Serializing the message field
Once you have created or updated you Field instance you just need to add it to your response header using the
toHttpValue method or the __toString() to generate the correct HTTP text representation to insert in your
message header.
// or you can use push an HTTP header $newCacheStatus = $cacheStatus->push('BrowserCache; fwd=uri-miss'); $newResponse = $response->withHeader(Field::NAME, $newCacheStatus); echo $response->getHeaderLine(Field::NAME);
While we used PSR-7 ResponseInterface, The package parsing and serializing methods can use any HTTP abstraction package or PHP $_SERVER array.
$cacheStatus = Field::fromSapiServer($_SERVER, Field::SAPI_NAME); $newCacheStatus = $cacheStatus->push('BrowserCache; fwd=uri-miss'); header(Field::NAME.': '.$newCacheStatus);
In this last example we use PHP native function to parse and add the correct header to the PHP emitted HTTP response.
Structured Fields
Because the Header field is compliant with the HTTP Structured Field RFC. We can easily parse it but were are also validating it against its specific RFC rules. To do so the package is dependent on the HTTP Structured Fields for PHP v2.0 package, which remove all the boilerplate needed for such header to be parsed, validated and manipulated in PHP while staying compliant with all the different RFCs.