1 <?php 2 3 namespace Kevinrob\GuzzleCache\Strategy; 4 5 use Kevinrob\GuzzleCache\CacheEntry; 6 use Kevinrob\GuzzleCache\KeyValueHttpHeader; 7 use Kevinrob\GuzzleCache\Storage\CacheStorageInterface; 8 use Psr\Http\Message\RequestInterface; 9 use Psr\Http\Message\ResponseInterface; 10 11 /** 12 * This strategy represents a "greedy" HTTP client. 13 * 14 * It can be used to cache responses in spite of any cache related response headers, 15 * but it SHOULDN'T be used unless absolutely necessary, e.g. when accessing 16 * badly designed APIs without Cache control. 17 * 18 * Obviously, this follows no RFC :(. 19 */ 20 class GreedyCacheStrategy extends PrivateCacheStrategy 21 { 22 const HEADER_TTL = 'X-Kevinrob-GuzzleCache-TTL'; 23 24 /** 25 * @var int 26 */ 27 protected $defaultTtl; 28 29 /** 30 * @var KeyValueHttpHeader 31 */ 32 private $varyHeaders; 33 34 public function __construct(CacheStorageInterface $cache = null, $defaultTtl, KeyValueHttpHeader $varyHeaders = null) 35 { 36 $this->defaultTtl = $defaultTtl; 37 $this->varyHeaders = $varyHeaders; 38 parent::__construct($cache); 39 } 40 41 protected function getCacheKey(RequestInterface $request, KeyValueHttpHeader $varyHeaders = null) 42 { 43 if (null === $varyHeaders || $varyHeaders->isEmpty()) { 44 return hash( 45 'sha256', 46 'greedy'.$request->getMethod().$request->getUri() 47 ); 48 } 49 50 $cacheHeaders = []; 51 foreach ($varyHeaders as $key => $value) { 52 if ($request->hasHeader($key)) { 53 $cacheHeaders[$key] = $request->getHeader($key); 54 } 55 } 56 57 return hash( 58 'sha256', 59 'greedy'.$request->getMethod().$request->getUri().json_encode($cacheHeaders) 60 ); 61 } 62 63 public function cache(RequestInterface $request, ResponseInterface $response) 64 { 65 $warningMessage = sprintf('%d - "%s" "%s"', 66 299, 67 'Cached although the response headers indicate not to do it!', 68 (new \DateTime())->format(\DateTime::RFC1123) 69 ); 70 71 $response = $response->withAddedHeader('Warning', $warningMessage); 72 73 if ($cacheObject = $this->getCacheObject($request, $response)) { 74 return $this->storage->save( 75 $this->getCacheKey($request, $this->varyHeaders), 76 $cacheObject 77 ); 78 } 79 80 return false; 81 } 82 83 protected function getCacheObject(RequestInterface $request, ResponseInterface $response) 84 { 85 if (!array_key_exists($response->getStatusCode(), $this->statusAccepted)) { 86 // Don't cache it 87 return null; 88 } 89 90 if (null !== $this->varyHeaders && $this->varyHeaders->has('*')) { 91 // This will never match with a request 92 return; 93 } 94 95 $response = $response->withoutHeader('Etag')->withoutHeader('Last-Modified'); 96 97 $ttl = $this->defaultTtl; 98 if ($request->hasHeader(self::HEADER_TTL)) { 99 $ttlHeaderValues = $request->getHeader(self::HEADER_TTL); 100 $ttl = (int)reset($ttlHeaderValues); 101 } 102 103 return new CacheEntry($request->withoutHeader(self::HEADER_TTL), $response, new \DateTime(sprintf('+%d seconds', $ttl))); 104 } 105 106 public function fetch(RequestInterface $request) 107 { 108 $cache = $this->storage->fetch($this->getCacheKey($request, $this->varyHeaders)); 109 return $cache; 110 } 111 112 /** 113 * {@inheritdoc} 114 */ 115 public function delete(RequestInterface $request) 116 { 117 return $this->storage->delete($this->getCacheKey($request)); 118 } 119 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body