[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/HttpCache/ -> Store.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   * This code is partially based on the Rack-Cache library by Ryan Tomayko,
   9   * which is released under the MIT license.
  10   *
  11   * For the full copyright and license information, please view the LICENSE
  12   * file that was distributed with this source code.
  13   */
  14  
  15  namespace Symfony\Component\HttpKernel\HttpCache;
  16  
  17  use Symfony\Component\HttpFoundation\Request;
  18  use Symfony\Component\HttpFoundation\Response;
  19  
  20  /**
  21   * Store implements all the logic for storing cache metadata (Request and Response headers).
  22   *
  23   * @author Fabien Potencier <fabien@symfony.com>
  24   */
  25  class Store implements StoreInterface
  26  {
  27      protected $root;
  28      private $keyCache;
  29      private $locks;
  30  
  31      /**
  32       * Constructor.
  33       *
  34       * @param string $root The path to the cache directory
  35       *
  36       * @throws \RuntimeException
  37       */
  38      public function __construct($root)
  39      {
  40          $this->root = $root;
  41          if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
  42              throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
  43          }
  44          $this->keyCache = new \SplObjectStorage();
  45          $this->locks = array();
  46      }
  47  
  48      /**
  49       * Cleanups storage.
  50       */
  51      public function cleanup()
  52      {
  53          // unlock everything
  54          foreach ($this->locks as $lock) {
  55              if (file_exists($lock)) {
  56                  @unlink($lock);
  57              }
  58          }
  59  
  60          $error = error_get_last();
  61          if (1 === $error['type'] && false === headers_sent()) {
  62              // send a 503
  63              header('HTTP/1.0 503 Service Unavailable');
  64              header('Retry-After: 10');
  65              echo '503 Service Unavailable';
  66          }
  67      }
  68  
  69      /**
  70       * Locks the cache for a given Request.
  71       *
  72       * @param Request $request A Request instance
  73       *
  74       * @return bool|string true if the lock is acquired, the path to the current lock otherwise
  75       */
  76      public function lock(Request $request)
  77      {
  78          $path = $this->getPath($this->getCacheKey($request).'.lck');
  79          if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
  80              return false;
  81          }
  82  
  83          $lock = @fopen($path, 'x');
  84          if (false !== $lock) {
  85              fclose($lock);
  86  
  87              $this->locks[] = $path;
  88  
  89              return true;
  90          }
  91  
  92          return !file_exists($path) ?: $path;
  93      }
  94  
  95      /**
  96       * Releases the lock for the given Request.
  97       *
  98       * @param Request $request A Request instance
  99       *
 100       * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise
 101       */
 102      public function unlock(Request $request)
 103      {
 104          $file = $this->getPath($this->getCacheKey($request).'.lck');
 105  
 106          return is_file($file) ? @unlink($file) : false;
 107      }
 108  
 109      public function isLocked(Request $request)
 110      {
 111          $path = $this->getPath($this->getCacheKey($request).'.lck');
 112          clearstatcache(true, $path);
 113  
 114          return is_file($path);
 115      }
 116  
 117      /**
 118       * Locates a cached Response for the Request provided.
 119       *
 120       * @param Request $request A Request instance
 121       *
 122       * @return Response|null A Response instance, or null if no cache entry was found
 123       */
 124      public function lookup(Request $request)
 125      {
 126          $key = $this->getCacheKey($request);
 127  
 128          if (!$entries = $this->getMetadata($key)) {
 129              return;
 130          }
 131  
 132          // find a cached entry that matches the request.
 133          $match = null;
 134          foreach ($entries as $entry) {
 135              if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? implode(', ', $entry[1]['vary']) : '', $request->headers->all(), $entry[0])) {
 136                  $match = $entry;
 137  
 138                  break;
 139              }
 140          }
 141  
 142          if (null === $match) {
 143              return;
 144          }
 145  
 146          list($req, $headers) = $match;
 147          if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) {
 148              return $this->restoreResponse($headers, $body);
 149          }
 150  
 151          // TODO the metaStore referenced an entity that doesn't exist in
 152          // the entityStore. We definitely want to return nil but we should
 153          // also purge the entry from the meta-store when this is detected.
 154      }
 155  
 156      /**
 157       * Writes a cache entry to the store for the given Request and Response.
 158       *
 159       * Existing entries are read and any that match the response are removed. This
 160       * method calls write with the new list of cache entries.
 161       *
 162       * @param Request  $request  A Request instance
 163       * @param Response $response A Response instance
 164       *
 165       * @return string The key under which the response is stored
 166       *
 167       * @throws \RuntimeException
 168       */
 169      public function write(Request $request, Response $response)
 170      {
 171          $key = $this->getCacheKey($request);
 172          $storedEnv = $this->persistRequest($request);
 173  
 174          // write the response body to the entity store if this is the original response
 175          if (!$response->headers->has('X-Content-Digest')) {
 176              $digest = $this->generateContentDigest($response);
 177  
 178              if (false === $this->save($digest, $response->getContent())) {
 179                  throw new \RuntimeException('Unable to store the entity.');
 180              }
 181  
 182              $response->headers->set('X-Content-Digest', $digest);
 183  
 184              if (!$response->headers->has('Transfer-Encoding')) {
 185                  $response->headers->set('Content-Length', strlen($response->getContent()));
 186              }
 187          }
 188  
 189          // read existing cache entries, remove non-varying, and add this one to the list
 190          $entries = array();
 191          $vary = $response->headers->get('vary');
 192          foreach ($this->getMetadata($key) as $entry) {
 193              if (!isset($entry[1]['vary'][0])) {
 194                  $entry[1]['vary'] = array('');
 195              }
 196  
 197              if ($vary != $entry[1]['vary'][0] || !$this->requestsMatch($vary, $entry[0], $storedEnv)) {
 198                  $entries[] = $entry;
 199              }
 200          }
 201  
 202          $headers = $this->persistResponse($response);
 203          unset($headers['age']);
 204  
 205          array_unshift($entries, array($storedEnv, $headers));
 206  
 207          if (false === $this->save($key, serialize($entries))) {
 208              throw new \RuntimeException('Unable to store the metadata.');
 209          }
 210  
 211          return $key;
 212      }
 213  
 214      /**
 215       * Returns content digest for $response.
 216       *
 217       * @param Response $response
 218       *
 219       * @return string
 220       */
 221      protected function generateContentDigest(Response $response)
 222      {
 223          return 'en'.sha1($response->getContent());
 224      }
 225  
 226      /**
 227       * Invalidates all cache entries that match the request.
 228       *
 229       * @param Request $request A Request instance
 230       *
 231       * @throws \RuntimeException
 232       */
 233      public function invalidate(Request $request)
 234      {
 235          $modified = false;
 236          $key = $this->getCacheKey($request);
 237  
 238          $entries = array();
 239          foreach ($this->getMetadata($key) as $entry) {
 240              $response = $this->restoreResponse($entry[1]);
 241              if ($response->isFresh()) {
 242                  $response->expire();
 243                  $modified = true;
 244                  $entries[] = array($entry[0], $this->persistResponse($response));
 245              } else {
 246                  $entries[] = $entry;
 247              }
 248          }
 249  
 250          if ($modified && false === $this->save($key, serialize($entries))) {
 251              throw new \RuntimeException('Unable to store the metadata.');
 252          }
 253      }
 254  
 255      /**
 256       * Determines whether two Request HTTP header sets are non-varying based on
 257       * the vary response header value provided.
 258       *
 259       * @param string $vary A Response vary header
 260       * @param array  $env1 A Request HTTP header array
 261       * @param array  $env2 A Request HTTP header array
 262       *
 263       * @return bool true if the two environments match, false otherwise
 264       */
 265      private function requestsMatch($vary, $env1, $env2)
 266      {
 267          if (empty($vary)) {
 268              return true;
 269          }
 270  
 271          foreach (preg_split('/[\s,]+/', $vary) as $header) {
 272              $key = str_replace('_', '-', strtolower($header));
 273              $v1 = isset($env1[$key]) ? $env1[$key] : null;
 274              $v2 = isset($env2[$key]) ? $env2[$key] : null;
 275              if ($v1 !== $v2) {
 276                  return false;
 277              }
 278          }
 279  
 280          return true;
 281      }
 282  
 283      /**
 284       * Gets all data associated with the given key.
 285       *
 286       * Use this method only if you know what you are doing.
 287       *
 288       * @param string $key The store key
 289       *
 290       * @return array An array of data associated with the key
 291       */
 292      private function getMetadata($key)
 293      {
 294          if (false === $entries = $this->load($key)) {
 295              return array();
 296          }
 297  
 298          return unserialize($entries);
 299      }
 300  
 301      /**
 302       * Purges data for the given URL.
 303       *
 304       * @param string $url A URL
 305       *
 306       * @return bool true if the URL exists and has been purged, false otherwise
 307       */
 308      public function purge($url)
 309      {
 310          if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) {
 311              unlink($path);
 312  
 313              return true;
 314          }
 315  
 316          return false;
 317      }
 318  
 319      /**
 320       * Loads data for the given key.
 321       *
 322       * @param string $key The store key
 323       *
 324       * @return string The data associated with the key
 325       */
 326      private function load($key)
 327      {
 328          $path = $this->getPath($key);
 329  
 330          return is_file($path) ? file_get_contents($path) : false;
 331      }
 332  
 333      /**
 334       * Save data for the given key.
 335       *
 336       * @param string $key  The store key
 337       * @param string $data The data to store
 338       *
 339       * @return bool
 340       */
 341      private function save($key, $data)
 342      {
 343          $path = $this->getPath($key);
 344          if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
 345              return false;
 346          }
 347  
 348          $tmpFile = tempnam(dirname($path), basename($path));
 349          if (false === $fp = @fopen($tmpFile, 'wb')) {
 350              return false;
 351          }
 352          @fwrite($fp, $data);
 353          @fclose($fp);
 354  
 355          if ($data != file_get_contents($tmpFile)) {
 356              return false;
 357          }
 358  
 359          if (false === @rename($tmpFile, $path)) {
 360              return false;
 361          }
 362  
 363          @chmod($path, 0666 & ~umask());
 364      }
 365  
 366      public function getPath($key)
 367      {
 368          return $this->root.DIRECTORY_SEPARATOR.substr($key, 0, 2).DIRECTORY_SEPARATOR.substr($key, 2, 2).DIRECTORY_SEPARATOR.substr($key, 4, 2).DIRECTORY_SEPARATOR.substr($key, 6);
 369      }
 370  
 371      /**
 372       * Returns a cache key for the given Request.
 373       *
 374       * @param Request $request A Request instance
 375       *
 376       * @return string A key for the given Request
 377       */
 378      private function getCacheKey(Request $request)
 379      {
 380          if (isset($this->keyCache[$request])) {
 381              return $this->keyCache[$request];
 382          }
 383  
 384          return $this->keyCache[$request] = 'md'.sha1($request->getUri());
 385      }
 386  
 387      /**
 388       * Persists the Request HTTP headers.
 389       *
 390       * @param Request $request A Request instance
 391       *
 392       * @return array An array of HTTP headers
 393       */
 394      private function persistRequest(Request $request)
 395      {
 396          return $request->headers->all();
 397      }
 398  
 399      /**
 400       * Persists the Response HTTP headers.
 401       *
 402       * @param Response $response A Response instance
 403       *
 404       * @return array An array of HTTP headers
 405       */
 406      private function persistResponse(Response $response)
 407      {
 408          $headers = $response->headers->all();
 409          $headers['X-Status'] = array($response->getStatusCode());
 410  
 411          return $headers;
 412      }
 413  
 414      /**
 415       * Restores a Response from the HTTP headers and body.
 416       *
 417       * @param array  $headers An array of HTTP headers for the Response
 418       * @param string $body    The Response body
 419       *
 420       * @return Response
 421       */
 422      private function restoreResponse($headers, $body = null)
 423      {
 424          $status = $headers['X-Status'][0];
 425          unset($headers['X-Status']);
 426  
 427          if (null !== $body) {
 428              $headers['X-Body-File'] = array($body);
 429          }
 430  
 431          return new Response($body, $status, $headers);
 432      }
 433  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1