@@ -51,7 +51,7 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
51
51
private array $ ageDirectives = [
52
52
'max-age ' => null ,
53
53
's-maxage ' => null ,
54
- 'expires ' => null ,
54
+ 'expires ' => false ,
55
55
];
56
56
57
57
public function add (Response $ response ): void
@@ -79,15 +79,30 @@ public function add(Response $response): void
79
79
return ;
80
80
}
81
81
82
- $ isHeuristicallyCacheable = $ response ->headers ->hasCacheControlDirective ('public ' );
83
82
$ maxAge = $ response ->headers ->hasCacheControlDirective ('max-age ' ) ? (int ) $ response ->headers ->getCacheControlDirective ('max-age ' ) : null ;
84
- $ this ->storeRelativeAgeDirective ('max-age ' , $ maxAge , $ age , $ isHeuristicallyCacheable );
85
83
$ sharedMaxAge = $ response ->headers ->hasCacheControlDirective ('s-maxage ' ) ? (int ) $ response ->headers ->getCacheControlDirective ('s-maxage ' ) : $ maxAge ;
86
- $ this ->storeRelativeAgeDirective ('s-maxage ' , $ sharedMaxAge , $ age , $ isHeuristicallyCacheable );
87
-
88
84
$ expires = $ response ->getExpires ();
89
85
$ expires = null !== $ expires ? (int ) $ expires ->format ('U ' ) - (int ) $ response ->getDate ()->format ('U ' ) : null ;
90
- $ this ->storeRelativeAgeDirective ('expires ' , $ expires >= 0 ? $ expires : null , 0 , $ isHeuristicallyCacheable );
86
+
87
+ // See https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.2
88
+ // If a response is "public" but does not have maximum lifetime, heuristics might be applied.
89
+ // Do not store NULL values so the final response can have more limiting value from other responses.
90
+ $ isHeuristicallyCacheable = $ response ->headers ->hasCacheControlDirective ('public ' )
91
+ && null === $ maxAge
92
+ && null === $ sharedMaxAge
93
+ && null === $ expires ;
94
+
95
+ if (!$ isHeuristicallyCacheable || null !== $ maxAge || null !== $ expires ) {
96
+ $ this ->storeRelativeAgeDirective ('max-age ' , $ maxAge , $ expires , $ age );
97
+ }
98
+
99
+ if (!$ isHeuristicallyCacheable || null !== $ sharedMaxAge || null !== $ expires ) {
100
+ $ this ->storeRelativeAgeDirective ('s-maxage ' , $ sharedMaxAge , $ expires , $ age );
101
+ }
102
+
103
+ if (null !== $ expires ) {
104
+ $ this ->ageDirectives ['expires ' ] = true ;
105
+ }
91
106
92
107
if (false !== $ this ->lastModified ) {
93
108
$ lastModified = $ response ->getLastModified ();
@@ -146,9 +161,9 @@ public function update(Response $response): void
146
161
}
147
162
}
148
163
149
- if (is_numeric ( $ this ->ageDirectives ['expires ' ]) ) {
164
+ if ($ this ->ageDirectives ['expires ' ] && null !== $ maxAge ) {
150
165
$ date = clone $ response ->getDate ();
151
- $ date = $ date ->modify ('+ ' .( $ this -> ageDirectives [ ' expires ' ] + $ this -> age ) .' seconds ' );
166
+ $ date = $ date ->modify ('+ ' .$ maxAge .' seconds ' );
152
167
$ response ->setExpires ($ date );
153
168
}
154
169
}
@@ -198,33 +213,16 @@ private function willMakeFinalResponseUncacheable(Response $response): bool
198
213
* we have to subtract the age so that the value is normalized for an age of 0.
199
214
*
200
215
* If the value is lower than the currently stored value, we update the value, to keep a rolling
201
- * minimal value of each instruction.
202
- *
203
- * If the value is NULL and the isHeuristicallyCacheable parameter is false, the directive will
204
- * not be set on the final response. In this case, not all responses had the directive set and no
205
- * value can be found that satisfies the requirements of all responses. The directive will be dropped
206
- * from the final response.
207
- *
208
- * If the isHeuristicallyCacheable parameter is true, however, the current response has been marked
209
- * as cacheable in a public (shared) cache, but did not provide an explicit lifetime that would serve
210
- * as an upper bound. In this case, we can proceed and possibly keep the directive on the final response.
216
+ * minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
211
217
*/
212
- private function storeRelativeAgeDirective (string $ directive , ?int $ value , int $ age , bool $ isHeuristicallyCacheable ): void
218
+ private function storeRelativeAgeDirective (string $ directive , ?int $ value , ? int $ expires , int $ age ): void
213
219
{
214
- if (null === $ value ) {
215
- if ($ isHeuristicallyCacheable ) {
216
- /*
217
- * See https://datatracker.ietf.org/doc/html/rfc7234#section-4.2.2
218
- * This particular response does not require maximum lifetime; heuristics might be applied.
219
- * Other responses, however, might have more stringent requirements on maximum lifetime.
220
- * So, return early here so that the final response can have the more limiting value set.
221
- */
222
- return ;
223
- }
220
+ if (null === $ value && null === $ expires ) {
224
221
$ this ->ageDirectives [$ directive ] = false ;
225
222
}
226
223
227
224
if (false !== $ this ->ageDirectives [$ directive ]) {
225
+ $ value = min ($ value ?? PHP_INT_MAX , $ expires ?? PHP_INT_MAX );
228
226
$ value -= $ age ;
229
227
$ this ->ageDirectives [$ directive ] = null !== $ this ->ageDirectives [$ directive ] ? min ($ this ->ageDirectives [$ directive ], $ value ) : $ value ;
230
228
}
0 commit comments