66use Http \Client \Common \Plugin \Exception \RewindStreamException ;
77use Http \Client \Common \Plugin \Cache \Generator \CacheKeyGenerator ;
88use Http \Client \Common \Plugin \Cache \Generator \SimpleGenerator ;
9+ use Http \Client \Common \Plugin \Cache \Listener \CacheListener ;
910use Http \Message \StreamFactory ;
1011use Http \Promise \FulfilledPromise ;
1112use Psr \Cache \CacheItemInterface ;
@@ -59,6 +60,8 @@ final class CachePlugin implements Plugin
5960 * @var array $methods list of request methods which can be cached
6061 * @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
6162 * @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
63+ * @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check.
64+ * Defaults to an empty array
6265 * }
6366 */
6467 public function __construct (CacheItemPoolInterface $ pool , StreamFactory $ streamFactory , array $ config = [])
@@ -129,7 +132,11 @@ public function handleRequest(RequestInterface $request, callable $next, callabl
129132 $ method = strtoupper ($ request ->getMethod ());
130133 // if the request not is cachable, move to $next
131134 if (!in_array ($ method , $ this ->config ['methods ' ])) {
132- return $ next ($ request );
135+ return $ next ($ request )->then (function (ResponseInterface $ response ) use ($ request ) {
136+ $ response = $ this ->handleCacheListeners ($ request , $ response , false , null );
137+
138+ return $ response ;
139+ });
133140 }
134141
135142 // If we can cache the request
@@ -141,7 +148,10 @@ public function handleRequest(RequestInterface $request, callable $next, callabl
141148 // The array_key_exists() is to be removed in 2.0.
142149 if (array_key_exists ('expiresAt ' , $ data ) && (null === $ data ['expiresAt ' ] || time () < $ data ['expiresAt ' ])) {
143150 // This item is still valid according to previous cache headers
144- return new FulfilledPromise ($ this ->createResponseFromCacheItem ($ cacheItem ));
151+ $ response = $ this ->createResponseFromCacheItem ($ cacheItem );
152+ $ response = $ this ->handleCacheListeners ($ request , $ response , true , $ cacheItem );
153+
154+ return new FulfilledPromise ($ response );
145155 }
146156
147157 // Add headers to ask the server if this cache is still valid
@@ -154,14 +164,14 @@ public function handleRequest(RequestInterface $request, callable $next, callabl
154164 }
155165 }
156166
157- return $ next ($ request )->then (function (ResponseInterface $ response ) use ($ cacheItem ) {
167+ return $ next ($ request )->then (function (ResponseInterface $ response ) use ($ request , $ cacheItem ) {
158168 if (304 === $ response ->getStatusCode ()) {
159169 if (!$ cacheItem ->isHit ()) {
160170 /*
161171 * We do not have the item in cache. This plugin did not add If-Modified-Since
162172 * or If-None-Match headers. Return the response from server.
163173 */
164- return $ response ;
174+ return $ this -> handleCacheListeners ( $ request , $ response, false , $ cacheItem ) ;
165175 }
166176
167177 // The cached response we have is still valid
@@ -171,7 +181,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl
171181 $ cacheItem ->set ($ data )->expiresAfter ($ this ->calculateCacheItemExpiresAfter ($ maxAge ));
172182 $ this ->pool ->save ($ cacheItem );
173183
174- return $ this ->createResponseFromCacheItem ($ cacheItem );
184+ return $ this ->handleCacheListeners ( $ request , $ this -> createResponseFromCacheItem ($ cacheItem ), true , $ cacheItem );
175185 }
176186
177187 if ($ this ->isCacheable ($ response )) {
@@ -196,7 +206,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl
196206 $ this ->pool ->save ($ cacheItem );
197207 }
198208
199- return $ response ;
209+ return $ this -> handleCacheListeners ( $ request , $ response, false , isset ( $ cacheItem ) ? $ cacheItem : null ) ;
200210 });
201211 }
202212
@@ -343,6 +353,7 @@ private function configureOptions(OptionsResolver $resolver)
343353 'methods ' => ['GET ' , 'HEAD ' ],
344354 'respect_response_cache_directives ' => ['no-cache ' , 'private ' , 'max-age ' , 'no-store ' ],
345355 'cache_key_generator ' => null ,
356+ 'cache_listeners ' => [],
346357 ]);
347358
348359 $ resolver ->setAllowedTypes ('cache_lifetime ' , ['int ' , 'null ' ]);
@@ -357,6 +368,7 @@ private function configureOptions(OptionsResolver $resolver)
357368
358369 return empty ($ matches );
359370 });
371+ $ resolver ->setAllowedTypes ('cache_listeners ' , ['array ' ]);
360372
361373 $ resolver ->setNormalizer ('respect_cache_headers ' , function (Options $ options , $ value ) {
362374 if (null !== $ value ) {
@@ -441,4 +453,23 @@ private function getETag(CacheItemInterface $cacheItem)
441453 }
442454 }
443455 }
456+
457+ /**
458+ * Call the cache listeners, if they are set.
459+ *
460+ * @param RequestInterface $request
461+ * @param ResponseInterface $response
462+ * @param bool $cacheHit
463+ * @param CacheItemInterface|null $cacheItem
464+ *
465+ * @return ResponseInterface
466+ */
467+ private function handleCacheListeners (RequestInterface $ request , ResponseInterface $ response , $ cacheHit , $ cacheItem )
468+ {
469+ foreach ($ this ->config ['cache_listeners ' ] as $ cacheListener ) {
470+ $ response = $ cacheListener ->onCacheResponse ($ request , $ response , $ cacheHit , $ cacheItem );
471+ }
472+
473+ return $ response ;
474+ }
444475}
0 commit comments