HTTP Cache
The caching system is used to store dynamic responses, utilizing If-None-Match (ETag) or If-Modified-Since
to allow sending an HTTP 304 response.
Caching Response
Starts the buffer to cache the response:
use Inphinit\Experimental\Http\Cache;
$cacher = new Cache();
$result = $cacher->start();
if ($result === Cache::FAILED) {
error_log('Cache failed', 0);
} elseif ($result === Cache::CACHED) {
// If it is already cached, processing can be terminated early
exit;
}
echo gmdate('M d Y H:i:s e'), "\n";
echo str_repeat("Hello!\n", 500);
// The use of stop() is generally optional, but it is recommended for greater control by the developer
$cacher->stop();
HTTP Response:
HTTP/1.1 200 OK
Host: localhost:5000
Cache-Control: public, max-age=3600
Expires: Thu, 01 Oct 2026 19:15:25 GMT
Last-Modified: Thu, 01 Oct 2026 19:14:55 GMT
Etag: "ec6bf505c3767e7598eb89876a52d97c86d42613c6b1a555d525a0a4c0b65601"
Content-type: text/html; charset=UTF-8
Development Environment
In the development environment, when the APP_ENVIRONMENT=development environment variable is used,
the X-Inphinit-Experimental-Cache header is sent to indicate whether the response was generated during
the current request or came from the stored cache, returning something like:
HTTP/1.1 200 OK
Host: localhost:5000
Cache-Control: public, max-age=3600
Expires: Thu, 01 Oct 2026 19:15:25 GMT
Last-Modified: Thu, 01 Oct 2026 19:14:55 GMT
Etag: "ec6bf505c3767e7598eb89876a52d97c86d42613c6b1a555d525a0a4c0b65601"
X-Inphinit-Experimental-Cache: writing
Content-type: text/html; charset=UTF-8
Possible values for the X-Inphinit-Experimental-Cache header:
| Header | Description |
|---|---|
X-Inphinit-Experimental-Cache: cached |
If the response for the current request is served from the cache |
X-Inphinit-Experimental-Cache: failed |
If caching the response fails |
X-Inphinit-Experimental-Cache: writing |
If the current response is not from the cache, but the process of writing to the cache has started for use in future requests |
Configuring storage
If you need to create separate caches for different users or scenarios to avoid conflicts, you can change the storage location, separating the caches into different directories as shown in the example:
use Inphinit\Experimental\Http\Cache;
use Inphinit\Experimental\Utility\Storage;
$user_path = 'storage/output/' . $user_id;
if (Storage::mkdir($user_path)) {
$cacher = new Cache(null, null, $user_path);
if ($cacher->start() === Cache::CACHED) {
exit;
}
} else {
$cacher = null;
}
echo gmdate('M d Y H:i:s e'), "\n";
echo str_repeat("Hello!\n", 500);
if ($cache !== null) {
$cacher->stop();
}
Running tests
To perform tests (such as unit tests) outside the web context, it is possible to explicitly pass the method and path values, as in the example:
use Inphinit\Experimental\Http\Cache;
use Inphinit\Experimental\Utility\Storage;
$tests_path = 'storage/output/tests/';
if (Storage::mkdir($tests_path) === false) {
throw \RuntimeException('Failed to create folder');
}
$cacher = new Cache('GET', '/fake_path?a=1&b=2', $tests_path);
$result = $cacher->start();
my_assert($result !== Cache::FAILED);
echo gmdate('M d Y H:i:s e'), "\n";
echo str_repeat("Hello!\n", 500);
$cacher->stop();
Using routes
Example of usage with routes:
use Inphinit\Experimental\Http\Cache;
$app->action(['GET', 'HEAD'], '/vehicle/<id:uuid>.html', function (App $app, array $params) {
$cacher = new Cache();
if ($cacher->start() === Cache::CACHED) {
exit;
}
echo 'Hour: ', gmdate('M d Y H:i:s e');
$cacher->stop();
});
Using route controllers
Controller example:
<?php
namespace Controllers;
use Inphinit\Experimental\Http\Cache;
class MyController
{
private $cacher;
public function __construct()
{
$this->cacher = new Cache();
if ($this->cacher->start() === Cache::CACHED) {
exit;
}
}
public function __destruct()
{
$this->cacher->stop();
}
public function foo($app, $params)
{
return 'Foo!';
}
public function bar($app, $params)
{
return 'Bar!';
}
}
Configuring routes:
<?php
$app->action('GET', '/sample/foo', 'MyController::foo');
$app->action('GET', '/sample/bar', 'MyController::bar');
Extending The Cache class
Example of class extension:
use Inphinit\Experimental\Http\Cache;
class MyOwnCache
{
protected static function match($etag, $modified)
{
// Validates only ETag, ignoring the use of If-Modified-Since
return Request::header('If-None-Match') === "\"{$etag}\"";
}
protected static function valid($method)
{
// Caches only requests using the HTTP QUERY method
return $method === 'QUERY';
}
protected static function createHash($path)
{
// Uses SHA-384
return \hash('sha384', $path);
}
}
$cacher = new MyOwnCache();
$result = $cacher->start();
if ($result === Cache::CACHED) {
exit;
}
...
API
Below is an explanation of how to use the methods of the Inphinit\Experimental\Http\Cache class:
| Usage | Description |
|---|---|
__construct(string $method = null, string $path = null, string $storage = null)
|
|
setWriter(callable $callback)
|
Sets a callback to overwrite the buffer on every flush (depends on the $chunkSize parameter in the start($expires, $chunkSize) method). |
start(int $expires = 3600, int $chunkSize = 1024): int
|
Starts the buffer to be written to the cache.
|
stop()
|
Ends the buffering. |
protected static function match(string $etag, int $modified): bool
|
Checks the If-None-Match header against the ETag and the If-Modified-Since header against the cache modification timestamp - Optionally be overridden in an extended class. |
protected static function valid(string $method): bool
|
Checks if the method is HEAD or GET - Optionally be overridden in an extended class to accept other methods. |
protected static function createHash(string $path): string
|
Creates the hashes used for caches and ETags - Optionally be overridden in an extended class. |