Skip to content

HttpCache

Viames Marino edited this page Oct 19, 2026 · 2 revisions

Pair framework: HttpCache

Pair\Http\HttpCache provides explicit helpers for HTTP cache validators on read responses.

Use it for endpoints where clients, browsers, or proxies can safely reuse a representation until its validator changes.

Helpers

  • HttpCache::etag(mixed $value, bool $weak = false): string
  • HttpCache::lastModified(DateTimeInterface|int|string $time): string
  • HttpCache::cacheControl(int $maxAge, string $visibility = 'public', bool $mustRevalidate = false): string
  • HttpCache::headers(?string $etag = null, DateTimeInterface|int|string|null $lastModified = null, ?string $cacheControl = null): array
  • HttpCache::isNotModified(?string $etag = null, DateTimeInterface|int|string|null $lastModified = null, ?array $server = null): bool
  • HttpCache::json(mixed $payload, int $httpCode = 200, ?string $etag = null, DateTimeInterface|int|string|null $lastModified = null, ?string $cacheControl = null, ?array $server = null): ResponseInterface
  • HttpCache::notModified(?string $etag = null, DateTimeInterface|int|string|null $lastModified = null, ?string $cacheControl = null): EmptyResponse

JSON read endpoint

This complete controller example uses a fixed public representation. In an application, load and authorize the real data before constructing the response or checking validators. Explicit v4 API controllers do not load a $this->model implicitly.

use Pair\Api\ApiController as PairApiController;
use Pair\Http\HttpCache;
use Pair\Http\ResponseInterface;

final class ApiController extends PairApiController {

	/**
	 * Return a public representation with explicit cache validators.
	 */
	public function showAction(): ResponseInterface {
		$data = ['id' => 7, 'title' => 'Example article'];
		$etag = HttpCache::etag($data);

		return HttpCache::json(
			$data,
			200,
			$etag,
			null,
			HttpCache::cacheControl(300, 'public')
		);
	}

}

When If-None-Match or If-Modified-Since matches the supplied validators, Pair returns 304 Not Modified through EmptyResponse. For mutable data, pass its real modification timestamp as the fourth argument when available. It must describe the selected representation; using the current time on every request prevents useful time-based revalidation.

List endpoint

For list responses, include every value that affects the representation in the ETag input:

$payload = [
	'data' => $rows,
	'meta' => [
		'page' => $page,
		'perPage' => $perPage,
		'total' => $total,
	],
];

$etag = HttpCache::etag([
	'query' => $_GET,
	'payload' => $payload,
]);

return HttpCache::json($payload, 200, $etag, null, HttpCache::cacheControl(60));

Generated CRUD resources

HTTP caching should stay explicit per action or resource policy. Do not enable it globally for all CRUD responses unless the resource has stable authorization, tenant scope, filters, and serialization.

For generated JSON resources, use HttpCache::json() in custom read actions or in future resource-level cache policy hooks.

Notes

  • Conditional matching is only considered for GET and HEAD.
  • If-None-Match takes precedence over If-Modified-Since.
  • HttpCache::etag() hashes the response representation and does not expose the payload in the header.
  • Cache-Control: no-store is available for sensitive responses.
  • Never cache user-specific or tenant-specific data as public unless the representation is safe for every recipient.
  • A conditional request still needs authentication and authorization before returning 304; validator matching does not establish access.

See also: JsonResponse, EmptyResponse, API, CrudController.

Clone this wiki locally