99use Http \Client \Common \Plugin \Cache \Listener \CacheListener ;
1010use Http \Message \StreamFactory ;
1111use Http \Promise \FulfilledPromise ;
12+ use Http \Promise \Promise ;
1213use Psr \Cache \CacheItemInterface ;
1314use Psr \Cache \CacheItemPoolInterface ;
1415use Psr \Http \Message \RequestInterface ;
@@ -39,20 +40,20 @@ final class CachePlugin implements Plugin
3940 private $ streamFactory ;
4041
4142 /**
42- * @var array
43+ * @var mixed[]
4344 */
4445 private $ config ;
4546
4647 /**
4748 * Cache directives indicating if a response can not be cached.
4849 *
49- * @var array
50+ * @var string[]
5051 */
5152 private $ noCacheFlags = ['no-cache ' , 'private ' , 'no-store ' ];
5253
5354 /**
5455 * @param StreamFactory|StreamFactoryInterface $streamFactory
55- * @param array $config {
56+ * @param mixed[] $config {
5657 *
5758 * @var bool $respect_cache_headers Whether to look at the cache directives or ignore them
5859 * @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
6162 * @var int $cache_lifetime (seconds) To support serving a previous stale response when the server answers 304
6263 * we have to store the cache for a longer time than the server originally says it is valid for.
6364 * 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
6768 * @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
6869 * @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check.
6970 * Defaults to an empty array
@@ -78,7 +79,7 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array
7879 $ this ->pool = $ pool ;
7980 $ this ->streamFactory = $ streamFactory ;
8081
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 )) {
8283 throw new \InvalidArgumentException ('You can \'t provide config option "respect_cache_headers" and "respect_response_cache_directives". Use "respect_response_cache_directives" instead. ' );
8384 }
8485
@@ -96,14 +97,14 @@ public function __construct(CacheItemPoolInterface $pool, $streamFactory, array
9697 * cache responses with `private` cache directive.
9798 *
9899 * @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
100101 *
101102 * @return CachePlugin
102103 */
103104 public static function clientCache (CacheItemPoolInterface $ pool , $ streamFactory , array $ config = [])
104105 {
105106 // Allow caching of private requests
106- if (isset ( $ config [ 'respect_response_cache_directives ' ] )) {
107+ if (\array_key_exists ( 'respect_response_cache_directives ' , $ config )) {
107108 $ config ['respect_response_cache_directives ' ][] = 'no-cache ' ;
108109 $ config ['respect_response_cache_directives ' ][] = 'max-age ' ;
109110 $ config ['respect_response_cache_directives ' ] = array_unique ($ config ['respect_response_cache_directives ' ]);
@@ -119,7 +120,7 @@ public static function clientCache(CacheItemPoolInterface $pool, $streamFactory,
119120 * cache responses with the `private`or `no-cache` directives.
120121 *
121122 * @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
123124 *
124125 * @return CachePlugin
125126 */
@@ -128,6 +129,11 @@ public static function serverCache(CacheItemPoolInterface $pool, $streamFactory,
128129 return new self ($ pool , $ streamFactory , $ config );
129130 }
130131
132+ /**
133+ * {@inheritdoc}
134+ *
135+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient)
136+ */
131137 protected function doHandleRequest (RequestInterface $ request , callable $ next , callable $ first )
132138 {
133139 $ method = strtoupper ($ request ->getMethod ());
@@ -146,22 +152,24 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
146152
147153 if ($ cacheItem ->isHit ()) {
148154 $ 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+ }
157164
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+ }
162169
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+ }
165173 }
166174 }
167175
@@ -207,22 +215,20 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
207215 $ this ->pool ->save ($ cacheItem );
208216 }
209217
210- return $ this ->handleCacheListeners ($ request , $ response , false , isset ( $ cacheItem) ? $ cacheItem : null );
218+ return $ this ->handleCacheListeners ($ request , $ response , false , $ cacheItem );
211219 });
212220 }
213221
214222 /**
215223 * Calculate the timestamp when this cache item should be dropped from the cache. The lowest value that can be
216224 * returned is $maxAge.
217225 *
218- * @param int|null $maxAge
219- *
220226 * @return int|null Unix system time passed to the PSR-6 cache
221227 */
222- private function calculateCacheItemExpiresAfter ($ maxAge )
228+ private function calculateCacheItemExpiresAfter (? int $ maxAge ): ? int
223229 {
224230 if (null === $ this ->config ['cache_lifetime ' ] && null === $ maxAge ) {
225- return ;
231+ return null ;
226232 }
227233
228234 return $ this ->config ['cache_lifetime ' ] + $ maxAge ;
@@ -232,14 +238,12 @@ private function calculateCacheItemExpiresAfter($maxAge)
232238 * Calculate the timestamp when a response expires. After that timestamp, we need to send a
233239 * If-Modified-Since / If-None-Match request to validate the response.
234240 *
235- * @param int|null $maxAge
236- *
237241 * @return int|null Unix system time. A null value means that the response expires when the cache item expires
238242 */
239- private function calculateResponseExpiresAt ($ maxAge )
243+ private function calculateResponseExpiresAt (? int $ maxAge ): ? int
240244 {
241245 if (null === $ maxAge ) {
242- return ;
246+ return null ;
243247 }
244248
245249 return time () + $ maxAge ;
@@ -268,10 +272,8 @@ protected function isCacheable(ResponseInterface $response)
268272
269273 /**
270274 * Verify that we can cache this request.
271- *
272- * @return bool
273275 */
274- private function isCacheableRequest (RequestInterface $ request )
276+ private function isCacheableRequest (RequestInterface $ request ): bool
275277 {
276278 $ uri = $ request ->getUri ()->__toString ();
277279 foreach ($ this ->config ['blacklisted_paths ' ] as $ regex ) {
@@ -290,7 +292,7 @@ private function isCacheableRequest(RequestInterface $request)
290292 *
291293 * @return bool|string The value of the directive, true if directive without value, false if directive not present
292294 */
293- private function getCacheControlDirective (ResponseInterface $ response , $ name )
295+ private function getCacheControlDirective (ResponseInterface $ response , string $ name )
294296 {
295297 $ headers = $ response ->getHeader ('Cache-Control ' );
296298 foreach ($ headers as $ header ) {
@@ -307,22 +309,19 @@ private function getCacheControlDirective(ResponseInterface $response, $name)
307309 return false ;
308310 }
309311
310- /**
311- * @return string
312- */
313- private function createCacheKey (RequestInterface $ request )
312+ private function createCacheKey (RequestInterface $ request ): string
314313 {
315314 $ key = $ this ->config ['cache_key_generator ' ]->generate ($ request );
316315
317316 return hash ($ this ->config ['hash_algo ' ], $ key );
318317 }
319318
320319 /**
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.
322321 *
323- * @return int| null
322+ * Returns null if we do not respect cache headers and got no defaultTtl.
324323 */
325- private function getMaxAge (ResponseInterface $ response )
324+ private function getMaxAge (ResponseInterface $ response ): ? int
326325 {
327326 if (!in_array ('max-age ' , $ this ->config ['respect_response_cache_directives ' ], true )) {
328327 return $ this ->config ['default_ttl ' ];
@@ -333,7 +332,7 @@ private function getMaxAge(ResponseInterface $response)
333332 if (!is_bool ($ maxAge )) {
334333 $ ageHeaders = $ response ->getHeader ('Age ' );
335334 foreach ($ ageHeaders as $ age ) {
336- return $ maxAge - ((int ) $ age );
335+ return (( int ) $ maxAge) - ((int ) $ age );
337336 }
338337
339338 return (int ) $ maxAge ;
@@ -351,7 +350,7 @@ private function getMaxAge(ResponseInterface $response)
351350 /**
352351 * Configure an options resolver.
353352 */
354- private function configureOptions (OptionsResolver $ resolver )
353+ private function configureOptions (OptionsResolver $ resolver ): void
355354 {
356355 $ resolver ->setDefaults ([
357356 'cache_lifetime ' => 86400 * 30 , // 30 days
@@ -398,10 +397,7 @@ private function configureOptions(OptionsResolver $resolver)
398397 });
399398 }
400399
401- /**
402- * @return ResponseInterface
403- */
404- private function createResponseFromCacheItem (CacheItemInterface $ cacheItem )
400+ private function createResponseFromCacheItem (CacheItemInterface $ cacheItem ): ResponseInterface
405401 {
406402 $ data = $ cacheItem ->get ();
407403
@@ -415,22 +411,18 @@ private function createResponseFromCacheItem(CacheItemInterface $cacheItem)
415411 throw new RewindStreamException ('Cannot rewind stream. ' , 0 , $ e );
416412 }
417413
418- $ response = $ response ->withBody ($ stream );
419-
420- return $ response ;
414+ return $ response ->withBody ($ stream );
421415 }
422416
423417 /**
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.
427419 */
428- private function getModifiedSinceHeaderValue (CacheItemInterface $ cacheItem )
420+ private function getModifiedSinceHeaderValue (CacheItemInterface $ cacheItem ): ? string
429421 {
430422 $ data = $ cacheItem ->get ();
431423 // The isset() is to be removed in 2.0.
432424 if (!isset ($ data ['createdAt ' ])) {
433- return ;
425+ return null ;
434426 }
435427
436428 $ modified = new \DateTime ('@ ' .$ data ['createdAt ' ]);
@@ -441,33 +433,28 @@ private function getModifiedSinceHeaderValue(CacheItemInterface $cacheItem)
441433
442434 /**
443435 * Get the ETag from the cached response.
444- *
445- * @return string|null
446436 */
447- private function getETag (CacheItemInterface $ cacheItem )
437+ private function getETag (CacheItemInterface $ cacheItem ): ? string
448438 {
449439 $ data = $ cacheItem ->get ();
450440 // The isset() is to be removed in 2.0.
451441 if (!isset ($ data ['etag ' ])) {
452- return ;
442+ return null ;
453443 }
454444
455445 foreach ($ data ['etag ' ] as $ etag ) {
456446 if (!empty ($ etag )) {
457447 return $ etag ;
458448 }
459449 }
450+
451+ return null ;
460452 }
461453
462454 /**
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.
469456 */
470- private function handleCacheListeners (RequestInterface $ request , ResponseInterface $ response , $ cacheHit , $ cacheItem )
457+ private function handleCacheListeners (RequestInterface $ request , ResponseInterface $ response , bool $ cacheHit , ? CacheItemInterface $ cacheItem ): ResponseInterface
471458 {
472459 foreach ($ this ->config ['cache_listeners ' ] as $ cacheListener ) {
473460 $ response = $ cacheListener ->onCacheResponse ($ request , $ response , $ cacheHit , $ cacheItem );
0 commit comments