9
9
use Http \Client \Common \Plugin \Cache \Listener \CacheListener ;
10
10
use Http \Message \StreamFactory ;
11
11
use Http \Promise \FulfilledPromise ;
12
+ use Http \Promise \Promise ;
12
13
use Psr \Cache \CacheItemInterface ;
13
14
use Psr \Cache \CacheItemPoolInterface ;
14
15
use Psr \Http \Message \RequestInterface ;
@@ -39,20 +40,20 @@ final class CachePlugin implements Plugin
39
40
private $ streamFactory ;
40
41
41
42
/**
42
- * @var array
43
+ * @var mixed[]
43
44
*/
44
45
private $ config ;
45
46
46
47
/**
47
48
* Cache directives indicating if a response can not be cached.
48
49
*
49
- * @var array
50
+ * @var string[]
50
51
*/
51
52
private $ noCacheFlags = ['no-cache ' , 'private ' , 'no-store ' ];
52
53
53
54
/**
54
55
* @param StreamFactory|StreamFactoryInterface $streamFactory
55
- * @param array $config {
56
+ * @param mixed[] $config {
56
57
*
57
58
* @var bool $respect_cache_headers Whether to look at the cache directives or ignore them
58
59
* @var int $default_ttl (seconds) If we do not respect cache headers or can't calculate a good ttl, use this
@@ -61,9 +62,9 @@ final class CachePlugin implements Plugin
61
62
* @var int $cache_lifetime (seconds) To support serving a previous stale response when the server answers 304
62
63
* we have to store the cache for a longer time than the server originally says it is valid for.
63
64
* We store a cache item for $cache_lifetime + max age of the response.
64
- * @var array $methods list of request methods which can be cached
65
- * @var array $blacklisted_paths list of regex for URLs explicitly not to be cached
66
- * @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
65
+ * @var string[] $methods list of request methods which can be cached
66
+ * @var string[] $blacklisted_paths list of regex for URLs explicitly not to be cached
67
+ * @var string[] $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
67
68
* @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
68
69
* @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check.
69
70
* Defaults to an empty array
@@ -78,7 +79,7 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array
78
79
$ this ->pool = $ pool ;
79
80
$ this ->streamFactory = $ streamFactory ;
80
81
81
- if (isset ( $ config [ 'respect_cache_headers ' ] ) && isset ( $ config [ 'respect_response_cache_directives ' ] )) {
82
+ if (\array_key_exists ( 'respect_cache_headers ' , $ config ) && \array_key_exists ( 'respect_response_cache_directives ' , $ config )) {
82
83
throw new \InvalidArgumentException ('You can \'t provide config option "respect_cache_headers" and "respect_response_cache_directives". Use "respect_response_cache_directives" instead. ' );
83
84
}
84
85
@@ -96,14 +97,14 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array
96
97
* cache responses with `private` cache directive.
97
98
*
98
99
* @param StreamFactory|StreamFactoryInterface $streamFactory
99
- * @param array $config For all possible config options see the constructor docs
100
+ * @param mixed[] $config For all possible config options see the constructor docs
100
101
*
101
102
* @return CachePlugin
102
103
*/
103
104
public static function clientCache (CacheItemPoolInterface $ pool , $ streamFactory , array $ config = [])
104
105
{
105
106
// Allow caching of private requests
106
- if (isset ( $ config [ 'respect_response_cache_directives ' ] )) {
107
+ if (\array_key_exists ( 'respect_response_cache_directives ' , $ config )) {
107
108
$ config ['respect_response_cache_directives ' ][] = 'no-cache ' ;
108
109
$ config ['respect_response_cache_directives ' ][] = 'max-age ' ;
109
110
$ config ['respect_response_cache_directives ' ] = array_unique ($ config ['respect_response_cache_directives ' ]);
@@ -119,7 +120,7 @@ public static function clientCache(CacheItemPoolInterface $pool, $streamFactory,
119
120
* cache responses with the `private`or `no-cache` directives.
120
121
*
121
122
* @param StreamFactory|StreamFactoryInterface $streamFactory
122
- * @param array $config For all possible config options see the constructor docs
123
+ * @param mixed[] $config For all possible config options see the constructor docs
123
124
*
124
125
* @return CachePlugin
125
126
*/
@@ -128,6 +129,11 @@ public static function serverCache(CacheItemPoolInterface $pool, $streamFactory,
128
129
return new self ($ pool , $ streamFactory , $ config );
129
130
}
130
131
132
+ /**
133
+ * {@inheritdoc}
134
+ *
135
+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient)
136
+ */
131
137
protected function doHandleRequest (RequestInterface $ request , callable $ next , callable $ first )
132
138
{
133
139
$ method = strtoupper ($ request ->getMethod ());
@@ -146,22 +152,24 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
146
152
147
153
if ($ cacheItem ->isHit ()) {
148
154
$ data = $ cacheItem ->get ();
149
- // The array_key_exists() is to be removed in 2.0.
150
- if (array_key_exists ('expiresAt ' , $ data ) && (null === $ data ['expiresAt ' ] || time () < $ data ['expiresAt ' ])) {
151
- // This item is still valid according to previous cache headers
152
- $ response = $ this ->createResponseFromCacheItem ($ cacheItem );
153
- $ response = $ this ->handleCacheListeners ($ request , $ response , true , $ cacheItem );
154
-
155
- return new FulfilledPromise ($ response );
156
- }
155
+ if (is_array ($ data )) {
156
+ // The array_key_exists() is to be removed in 2.0.
157
+ if (array_key_exists ('expiresAt ' , $ data ) && (null === $ data ['expiresAt ' ] || time () < $ data ['expiresAt ' ])) {
158
+ // This item is still valid according to previous cache headers
159
+ $ response = $ this ->createResponseFromCacheItem ($ cacheItem );
160
+ $ response = $ this ->handleCacheListeners ($ request , $ response , true , $ cacheItem );
161
+
162
+ return new FulfilledPromise ($ response );
163
+ }
157
164
158
- // Add headers to ask the server if this cache is still valid
159
- if ($ modifiedSinceValue = $ this ->getModifiedSinceHeaderValue ($ cacheItem )) {
160
- $ request = $ request ->withHeader ('If-Modified-Since ' , $ modifiedSinceValue );
161
- }
165
+ // Add headers to ask the server if this cache is still valid
166
+ if ($ modifiedSinceValue = $ this ->getModifiedSinceHeaderValue ($ cacheItem )) {
167
+ $ request = $ request ->withHeader ('If-Modified-Since ' , $ modifiedSinceValue );
168
+ }
162
169
163
- if ($ etag = $ this ->getETag ($ cacheItem )) {
164
- $ request = $ request ->withHeader ('If-None-Match ' , $ etag );
170
+ if ($ etag = $ this ->getETag ($ cacheItem )) {
171
+ $ request = $ request ->withHeader ('If-None-Match ' , $ etag );
172
+ }
165
173
}
166
174
}
167
175
@@ -207,22 +215,20 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
207
215
$ this ->pool ->save ($ cacheItem );
208
216
}
209
217
210
- return $ this ->handleCacheListeners ($ request , $ response , false , isset ( $ cacheItem) ? $ cacheItem : null );
218
+ return $ this ->handleCacheListeners ($ request , $ response , false , $ cacheItem );
211
219
});
212
220
}
213
221
214
222
/**
215
223
* Calculate the timestamp when this cache item should be dropped from the cache. The lowest value that can be
216
224
* returned is $maxAge.
217
225
*
218
- * @param int|null $maxAge
219
- *
220
226
* @return int|null Unix system time passed to the PSR-6 cache
221
227
*/
222
- private function calculateCacheItemExpiresAfter ($ maxAge )
228
+ private function calculateCacheItemExpiresAfter (? int $ maxAge ): ? int
223
229
{
224
230
if (null === $ this ->config ['cache_lifetime ' ] && null === $ maxAge ) {
225
- return ;
231
+ return null ;
226
232
}
227
233
228
234
return $ this ->config ['cache_lifetime ' ] + $ maxAge ;
@@ -232,14 +238,12 @@ private function calculateCacheItemExpiresAfter($maxAge)
232
238
* Calculate the timestamp when a response expires. After that timestamp, we need to send a
233
239
* If-Modified-Since / If-None-Match request to validate the response.
234
240
*
235
- * @param int|null $maxAge
236
- *
237
241
* @return int|null Unix system time. A null value means that the response expires when the cache item expires
238
242
*/
239
- private function calculateResponseExpiresAt ($ maxAge )
243
+ private function calculateResponseExpiresAt (? int $ maxAge ): ? int
240
244
{
241
245
if (null === $ maxAge ) {
242
- return ;
246
+ return null ;
243
247
}
244
248
245
249
return time () + $ maxAge ;
@@ -268,10 +272,8 @@ protected function isCacheable(ResponseInterface $response)
268
272
269
273
/**
270
274
* Verify that we can cache this request.
271
- *
272
- * @return bool
273
275
*/
274
- private function isCacheableRequest (RequestInterface $ request )
276
+ private function isCacheableRequest (RequestInterface $ request ): bool
275
277
{
276
278
$ uri = $ request ->getUri ()->__toString ();
277
279
foreach ($ this ->config ['blacklisted_paths ' ] as $ regex ) {
@@ -290,7 +292,7 @@ private function isCacheableRequest(RequestInterface $request)
290
292
*
291
293
* @return bool|string The value of the directive, true if directive without value, false if directive not present
292
294
*/
293
- private function getCacheControlDirective (ResponseInterface $ response , $ name )
295
+ private function getCacheControlDirective (ResponseInterface $ response , string $ name )
294
296
{
295
297
$ headers = $ response ->getHeader ('Cache-Control ' );
296
298
foreach ($ headers as $ header ) {
@@ -307,22 +309,19 @@ private function getCacheControlDirective(ResponseInterface $response, $name)
307
309
return false ;
308
310
}
309
311
310
- /**
311
- * @return string
312
- */
313
- private function createCacheKey (RequestInterface $ request )
312
+ private function createCacheKey (RequestInterface $ request ): string
314
313
{
315
314
$ key = $ this ->config ['cache_key_generator ' ]->generate ($ request );
316
315
317
316
return hash ($ this ->config ['hash_algo ' ], $ key );
318
317
}
319
318
320
319
/**
321
- * Get a ttl in seconds. It could return null if we do not respect cache headers and got no defaultTtl.
320
+ * Get a ttl in seconds.
322
321
*
323
- * @return int| null
322
+ * Returns null if we do not respect cache headers and got no defaultTtl.
324
323
*/
325
- private function getMaxAge (ResponseInterface $ response )
324
+ private function getMaxAge (ResponseInterface $ response ): ? int
326
325
{
327
326
if (!in_array ('max-age ' , $ this ->config ['respect_response_cache_directives ' ], true )) {
328
327
return $ this ->config ['default_ttl ' ];
@@ -333,7 +332,7 @@ private function getMaxAge(ResponseInterface $response)
333
332
if (!is_bool ($ maxAge )) {
334
333
$ ageHeaders = $ response ->getHeader ('Age ' );
335
334
foreach ($ ageHeaders as $ age ) {
336
- return $ maxAge - ((int ) $ age );
335
+ return (( int ) $ maxAge) - ((int ) $ age );
337
336
}
338
337
339
338
return (int ) $ maxAge ;
@@ -351,7 +350,7 @@ private function getMaxAge(ResponseInterface $response)
351
350
/**
352
351
* Configure an options resolver.
353
352
*/
354
- private function configureOptions (OptionsResolver $ resolver )
353
+ private function configureOptions (OptionsResolver $ resolver ): void
355
354
{
356
355
$ resolver ->setDefaults ([
357
356
'cache_lifetime ' => 86400 * 30 , // 30 days
@@ -398,10 +397,7 @@ private function configureOptions(OptionsResolver $resolver)
398
397
});
399
398
}
400
399
401
- /**
402
- * @return ResponseInterface
403
- */
404
- private function createResponseFromCacheItem (CacheItemInterface $ cacheItem )
400
+ private function createResponseFromCacheItem (CacheItemInterface $ cacheItem ): ResponseInterface
405
401
{
406
402
$ data = $ cacheItem ->get ();
407
403
@@ -415,22 +411,18 @@ private function createResponseFromCacheItem(CacheItemInterface $cacheItem)
415
411
throw new RewindStreamException ('Cannot rewind stream. ' , 0 , $ e );
416
412
}
417
413
418
- $ response = $ response ->withBody ($ stream );
419
-
420
- return $ response ;
414
+ return $ response ->withBody ($ stream );
421
415
}
422
416
423
417
/**
424
- * Get the value of the "If-Modified-Since" header.
425
- *
426
- * @return string|null
418
+ * Get the value for the "If-Modified-Since" header.
427
419
*/
428
- private function getModifiedSinceHeaderValue (CacheItemInterface $ cacheItem )
420
+ private function getModifiedSinceHeaderValue (CacheItemInterface $ cacheItem ): ? string
429
421
{
430
422
$ data = $ cacheItem ->get ();
431
423
// The isset() is to be removed in 2.0.
432
424
if (!isset ($ data ['createdAt ' ])) {
433
- return ;
425
+ return null ;
434
426
}
435
427
436
428
$ modified = new \DateTime ('@ ' .$ data ['createdAt ' ]);
@@ -441,33 +433,28 @@ private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem)
441
433
442
434
/**
443
435
* Get the ETag from the cached response.
444
- *
445
- * @return string|null
446
436
*/
447
- private function getETag (CacheItemInterface $ cacheItem )
437
+ private function getETag (CacheItemInterface $ cacheItem ): ? string
448
438
{
449
439
$ data = $ cacheItem ->get ();
450
440
// The isset() is to be removed in 2.0.
451
441
if (!isset ($ data ['etag ' ])) {
452
- return ;
442
+ return null ;
453
443
}
454
444
455
445
foreach ($ data ['etag ' ] as $ etag ) {
456
446
if (!empty ($ etag )) {
457
447
return $ etag ;
458
448
}
459
449
}
450
+
451
+ return null ;
460
452
}
461
453
462
454
/**
463
- * Call the cache listeners, if they are set.
464
- *
465
- * @param bool $cacheHit
466
- * @param CacheItemInterface|null $cacheItem
467
- *
468
- * @return ResponseInterface
455
+ * Call the registered cache listeners.
469
456
*/
470
- private function handleCacheListeners (RequestInterface $ request , ResponseInterface $ response , $ cacheHit , $ cacheItem )
457
+ private function handleCacheListeners (RequestInterface $ request , ResponseInterface $ response , bool $ cacheHit , ? CacheItemInterface $ cacheItem ): ResponseInterface
471
458
{
472
459
foreach ($ this ->config ['cache_listeners ' ] as $ cacheListener ) {
473
460
$ response = $ cacheListener ->onCacheResponse ($ request , $ response , $ cacheHit , $ cacheItem );
0 commit comments