Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   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  }