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