[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/symfony/http-kernel/HttpCache/ -> ResponseCacheStrategy.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\HttpKernel\HttpCache;
  13  
  14  use Symfony\Component\HttpFoundation\Response;
  15  
  16  /**
  17   * ResponseCacheStrategy knows how to compute the Response cache HTTP header
  18   * based on the different response cache headers.
  19   *
  20   * This implementation changes the master response TTL to the smallest TTL received
  21   * or force validation if one of the surrogates has validation cache strategy.
  22   *
  23   * @author Fabien Potencier <fabien@symfony.com>
  24   */
  25  class ResponseCacheStrategy implements ResponseCacheStrategyInterface
  26  {
  27      /**
  28       * Cache-Control headers that are sent to the final response if they appear in ANY of the responses.
  29       */
  30      private static $overrideDirectives = ['private', 'no-cache', 'no-store', 'no-transform', 'must-revalidate', 'proxy-revalidate'];
  31  
  32      /**
  33       * Cache-Control headers that are sent to the final response if they appear in ALL of the responses.
  34       */
  35      private static $inheritDirectives = ['public', 'immutable'];
  36  
  37      private $embeddedResponses = 0;
  38      private $isNotCacheableResponseEmbedded = false;
  39      private $age = 0;
  40      private $flagDirectives = [
  41          'no-cache' => null,
  42          'no-store' => null,
  43          'no-transform' => null,
  44          'must-revalidate' => null,
  45          'proxy-revalidate' => null,
  46          'public' => null,
  47          'private' => null,
  48          'immutable' => null,
  49      ];
  50      private $ageDirectives = [
  51          'max-age' => null,
  52          's-maxage' => null,
  53          'expires' => null,
  54      ];
  55  
  56      /**
  57       * {@inheritdoc}
  58       */
  59      public function add(Response $response)
  60      {
  61          ++$this->embeddedResponses;
  62  
  63          foreach (self::$overrideDirectives as $directive) {
  64              if ($response->headers->hasCacheControlDirective($directive)) {
  65                  $this->flagDirectives[$directive] = true;
  66              }
  67          }
  68  
  69          foreach (self::$inheritDirectives as $directive) {
  70              if (false !== $this->flagDirectives[$directive]) {
  71                  $this->flagDirectives[$directive] = $response->headers->hasCacheControlDirective($directive);
  72              }
  73          }
  74  
  75          $age = $response->getAge();
  76          $this->age = max($this->age, $age);
  77  
  78          if ($this->willMakeFinalResponseUncacheable($response)) {
  79              $this->isNotCacheableResponseEmbedded = true;
  80  
  81              return;
  82          }
  83  
  84          $this->storeRelativeAgeDirective('max-age', $response->headers->getCacheControlDirective('max-age'), $age);
  85          $this->storeRelativeAgeDirective('s-maxage', $response->headers->getCacheControlDirective('s-maxage') ?: $response->headers->getCacheControlDirective('max-age'), $age);
  86  
  87          $expires = $response->getExpires();
  88          $expires = null !== $expires ? (int) $expires->format('U') - (int) $response->getDate()->format('U') : null;
  89          $this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0);
  90      }
  91  
  92      /**
  93       * {@inheritdoc}
  94       */
  95      public function update(Response $response)
  96      {
  97          // if we have no embedded Response, do nothing
  98          if (0 === $this->embeddedResponses) {
  99              return;
 100          }
 101  
 102          // Remove validation related headers of the master response,
 103          // because some of the response content comes from at least
 104          // one embedded response (which likely has a different caching strategy).
 105          $response->setEtag(null);
 106          $response->setLastModified(null);
 107  
 108          $this->add($response);
 109  
 110          $response->headers->set('Age', $this->age);
 111  
 112          if ($this->isNotCacheableResponseEmbedded) {
 113              if ($this->flagDirectives['no-store']) {
 114                  $response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
 115              } else {
 116                  $response->headers->set('Cache-Control', 'no-cache, must-revalidate');
 117              }
 118  
 119              return;
 120          }
 121  
 122          $flags = array_filter($this->flagDirectives);
 123  
 124          if (isset($flags['must-revalidate'])) {
 125              $flags['no-cache'] = true;
 126          }
 127  
 128          $response->headers->set('Cache-Control', implode(', ', array_keys($flags)));
 129  
 130          $maxAge = null;
 131  
 132          if (is_numeric($this->ageDirectives['max-age'])) {
 133              $maxAge = $this->ageDirectives['max-age'] + $this->age;
 134              $response->headers->addCacheControlDirective('max-age', $maxAge);
 135          }
 136  
 137          if (is_numeric($this->ageDirectives['s-maxage'])) {
 138              $sMaxage = $this->ageDirectives['s-maxage'] + $this->age;
 139  
 140              if ($maxAge !== $sMaxage) {
 141                  $response->headers->addCacheControlDirective('s-maxage', $sMaxage);
 142              }
 143          }
 144  
 145          if (is_numeric($this->ageDirectives['expires'])) {
 146              $date = clone $response->getDate();
 147              $date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
 148              $response->setExpires($date);
 149          }
 150      }
 151  
 152      /**
 153       * RFC2616, Section 13.4.
 154       *
 155       * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
 156       *
 157       * @return bool
 158       */
 159      private function willMakeFinalResponseUncacheable(Response $response)
 160      {
 161          // RFC2616: A response received with a status code of 200, 203, 300, 301 or 410
 162          // MAY be stored by a cache […] unless a cache-control directive prohibits caching.
 163          if ($response->headers->hasCacheControlDirective('no-cache')
 164              || $response->headers->getCacheControlDirective('no-store')
 165          ) {
 166              return true;
 167          }
 168  
 169          // Last-Modified and Etag headers cannot be merged, they render the response uncacheable
 170          // by default (except if the response also has max-age etc.).
 171          if (\in_array($response->getStatusCode(), [200, 203, 300, 301, 410])
 172              && null === $response->getLastModified()
 173              && null === $response->getEtag()
 174          ) {
 175              return false;
 176          }
 177  
 178          // RFC2616: A response received with any other status code (e.g. status codes 302 and 307)
 179          // MUST NOT be returned in a reply to a subsequent request unless there are
 180          // cache-control directives or another header(s) that explicitly allow it.
 181          $cacheControl = ['max-age', 's-maxage', 'must-revalidate', 'proxy-revalidate', 'public', 'private'];
 182          foreach ($cacheControl as $key) {
 183              if ($response->headers->hasCacheControlDirective($key)) {
 184                  return false;
 185              }
 186          }
 187  
 188          if ($response->headers->has('Expires')) {
 189              return false;
 190          }
 191  
 192          return true;
 193      }
 194  
 195      /**
 196       * Store lowest max-age/s-maxage/expires for the final response.
 197       *
 198       * The response might have been stored in cache a while ago. To keep things comparable,
 199       * we have to subtract the age so that the value is normalized for an age of 0.
 200       *
 201       * If the value is lower than the currently stored value, we update the value, to keep a rolling
 202       * minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
 203       *
 204       * @param string   $directive
 205       * @param int|null $value
 206       * @param int      $age
 207       */
 208      private function storeRelativeAgeDirective($directive, $value, $age)
 209      {
 210          if (null === $value) {
 211              $this->ageDirectives[$directive] = false;
 212          }
 213  
 214          if (false !== $this->ageDirectives[$directive]) {
 215              $value -= $age;
 216              $this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
 217          }
 218      }
 219  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1