This commit is contained in:
2025-05-12 14:25:25 +02:00
parent ab2db755ef
commit 9e378ca2b7
2719 changed files with 46505 additions and 60181 deletions

View File

@@ -51,7 +51,7 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
private array $ageDirectives = [
'max-age' => null,
's-maxage' => null,
'expires' => null,
'expires' => false,
];
/**
@@ -82,15 +82,30 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
return;
}
$isHeuristicallyCacheable = $response->headers->hasCacheControlDirective('public');
$maxAge = $response->headers->hasCacheControlDirective('max-age') ? (int) $response->headers->getCacheControlDirective('max-age') : null;
$this->storeRelativeAgeDirective('max-age', $maxAge, $age, $isHeuristicallyCacheable);
$sharedMaxAge = $response->headers->hasCacheControlDirective('s-maxage') ? (int) $response->headers->getCacheControlDirective('s-maxage') : $maxAge;
$this->storeRelativeAgeDirective('s-maxage', $sharedMaxAge, $age, $isHeuristicallyCacheable);
$expires = $response->getExpires();
$expires = null !== $expires ? (int) $expires->format('U') - (int) $response->getDate()->format('U') : null;
$this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0, $isHeuristicallyCacheable);
// See https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.2
// If a response is "public" but does not have maximum lifetime, heuristics might be applied.
// Do not store NULL values so the final response can have more limiting value from other responses.
$isHeuristicallyCacheable = $response->headers->hasCacheControlDirective('public')
&& null === $maxAge
&& null === $sharedMaxAge
&& null === $expires;
if (!$isHeuristicallyCacheable || null !== $maxAge || null !== $expires) {
$this->storeRelativeAgeDirective('max-age', $maxAge, $expires, $age);
}
if (!$isHeuristicallyCacheable || null !== $sharedMaxAge || null !== $expires) {
$this->storeRelativeAgeDirective('s-maxage', $sharedMaxAge, $expires, $age);
}
if (null !== $expires) {
$this->ageDirectives['expires'] = true;
}
if (false !== $this->lastModified) {
$lastModified = $response->getLastModified();
@@ -152,9 +167,9 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
}
}
if (is_numeric($this->ageDirectives['expires'])) {
if ($this->ageDirectives['expires'] && null !== $maxAge) {
$date = clone $response->getDate();
$date = $date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
$date = $date->modify('+'.$maxAge.' seconds');
$response->setExpires($date);
}
}
@@ -204,33 +219,16 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
* we have to subtract the age so that the value is normalized for an age of 0.
*
* If the value is lower than the currently stored value, we update the value, to keep a rolling
* minimal value of each instruction.
*
* If the value is NULL and the isHeuristicallyCacheable parameter is false, the directive will
* not be set on the final response. In this case, not all responses had the directive set and no
* value can be found that satisfies the requirements of all responses. The directive will be dropped
* from the final response.
*
* If the isHeuristicallyCacheable parameter is true, however, the current response has been marked
* as cacheable in a public (shared) cache, but did not provide an explicit lifetime that would serve
* as an upper bound. In this case, we can proceed and possibly keep the directive on the final response.
* minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
*/
private function storeRelativeAgeDirective(string $directive, ?int $value, int $age, bool $isHeuristicallyCacheable): void
private function storeRelativeAgeDirective(string $directive, ?int $value, ?int $expires, int $age): void
{
if (null === $value) {
if ($isHeuristicallyCacheable) {
/*
* See https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.2
* This particular response does not require maximum lifetime; heuristics might be applied.
* Other responses, however, might have more stringent requirements on maximum lifetime.
* So, return early here so that the final response can have the more limiting value set.
*/
return;
}
if (null === $value && null === $expires) {
$this->ageDirectives[$directive] = false;
}
if (false !== $this->ageDirectives[$directive]) {
$value = min($value ?? PHP_INT_MAX, $expires ?? PHP_INT_MAX);
$value -= $age;
$this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
}