Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Store.php

Zuletzt modifiziert: 09.10.2024, 12:56 - Dateigröße: 13.56 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of the Symfony package.
 005   *
 006   * (c) Fabien Potencier <fabien@symfony.com>
 007   *
 008   * This code is partially based on the Rack-Cache library by Ryan Tomayko,
 009   * which is released under the MIT license.
 010   *
 011   * For the full copyright and license information, please view the LICENSE
 012   * file that was distributed with this source code.
 013   */
 014   
 015  namespace Symfony\Component\HttpKernel\HttpCache;
 016   
 017  use Symfony\Component\HttpFoundation\Request;
 018  use Symfony\Component\HttpFoundation\Response;
 019   
 020  /**
 021   * Store implements all the logic for storing cache metadata (Request and Response headers).
 022   *
 023   * @author Fabien Potencier <fabien@symfony.com>
 024   */
 025  class Store implements StoreInterface
 026  {
 027      protected $root;
 028      private $keyCache;
 029      private $locks;
 030   
 031      /**
 032       * Constructor.
 033       *
 034       * @param string $root The path to the cache directory
 035       *
 036       * @throws \RuntimeException
 037       */
 038      public function __construct($root)
 039      {
 040          $this->root = $root;
 041          if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
 042              throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
 043          }
 044          $this->keyCache = new \SplObjectStorage();
 045          $this->locks = array();
 046      }
 047   
 048      /**
 049       * Cleanups storage.
 050       */
 051      public function cleanup()
 052      {
 053          // unlock everything
 054          foreach ($this->locks as $lock) {
 055              flock($lock, LOCK_UN);
 056              fclose($lock);
 057          }
 058   
 059          $this->locks = array();
 060      }
 061   
 062      /**
 063       * Tries to lock the cache for a given Request, without blocking.
 064       *
 065       * @param Request $request A Request instance
 066       *
 067       * @return bool|string true if the lock is acquired, the path to the current lock otherwise
 068       */
 069      public function lock(Request $request)
 070      {
 071          $key = $this->getCacheKey($request);
 072   
 073          if (!isset($this->locks[$key])) {
 074              $path = $this->getPath($key);
 075              if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
 076                  return $path;
 077              }
 078              $h = fopen($path, 'cb');
 079              if (!flock($h, LOCK_EX | LOCK_NB)) {
 080                  fclose($h);
 081   
 082                  return $path;
 083              }
 084   
 085              $this->locks[$key] = $h;
 086          }
 087   
 088          return true;
 089      }
 090   
 091      /**
 092       * Releases the lock for the given Request.
 093       *
 094       * @param Request $request A Request instance
 095       *
 096       * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise
 097       */
 098      public function unlock(Request $request)
 099      {
 100          $key = $this->getCacheKey($request);
 101   
 102          if (isset($this->locks[$key])) {
 103              flock($this->locks[$key], LOCK_UN);
 104              fclose($this->locks[$key]);
 105              unset($this->locks[$key]);
 106   
 107              return true;
 108          }
 109   
 110          return false;
 111      }
 112   
 113      public function isLocked(Request $request)
 114      {
 115          $key = $this->getCacheKey($request);
 116   
 117          if (isset($this->locks[$key])) {
 118              return true; // shortcut if lock held by this process
 119          }
 120   
 121          if (!file_exists($path = $this->getPath($key))) {
 122              return false;
 123          }
 124   
 125          $h = fopen($path, 'rb');
 126          flock($h, LOCK_EX | LOCK_NB, $wouldBlock);
 127          flock($h, LOCK_UN); // release the lock we just acquired
 128          fclose($h);
 129   
 130          return (bool) $wouldBlock;
 131      }
 132   
 133      /**
 134       * Locates a cached Response for the Request provided.
 135       *
 136       * @param Request $request A Request instance
 137       *
 138       * @return Response|null A Response instance, or null if no cache entry was found
 139       */
 140      public function lookup(Request $request)
 141      {
 142          $key = $this->getCacheKey($request);
 143   
 144          if (!$entries = $this->getMetadata($key)) {
 145              return;
 146          }
 147   
 148          // find a cached entry that matches the request.
 149          $match = null;
 150          foreach ($entries as $entry) {
 151              if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? implode(', ', $entry[1]['vary']) : '', $request->headers->all(), $entry[0])) {
 152                  $match = $entry;
 153   
 154                  break;
 155              }
 156          }
 157   
 158          if (null === $match) {
 159              return;
 160          }
 161   
 162          list($req, $headers) = $match;
 163          if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
 164              return $this->restoreResponse($headers, $body);
 165          }
 166   
 167          // TODO the metaStore referenced an entity that doesn't exist in
 168          // the entityStore. We definitely want to return nil but we should
 169          // also purge the entry from the meta-store when this is detected.
 170      }
 171   
 172      /**
 173       * Writes a cache entry to the store for the given Request and Response.
 174       *
 175       * Existing entries are read and any that match the response are removed. This
 176       * method calls write with the new list of cache entries.
 177       *
 178       * @param Request  $request  A Request instance
 179       * @param Response $response A Response instance
 180       *
 181       * @return string The key under which the response is stored
 182       *
 183       * @throws \RuntimeException
 184       */
 185      public function write(Request $request, Response $response)
 186      {
 187          $key = $this->getCacheKey($request);
 188          $storedEnv = $this->persistRequest($request);
 189   
 190          // write the response body to the entity store if this is the original response
 191          if (!$response->headers->has('X-Content-Digest')) {
 192              $digest = $this->generateContentDigest($response);
 193   
 194              if (false === $this->save($digest, $response->getContent())) {
 195                  throw new \RuntimeException('Unable to store the entity.');
 196              }
 197   
 198              $response->headers->set('X-Content-Digest', $digest);
 199   
 200              if (!$response->headers->has('Transfer-Encoding')) {
 201                  $response->headers->set('Content-Length', strlen($response->getContent()));
 202              }
 203          }
 204   
 205          // read existing cache entries, remove non-varying, and add this one to the list
 206          $entries = array();
 207          $vary = $response->headers->get('vary');
 208          foreach ($this->getMetadata($key) as $entry) {
 209              if (!isset($entry[1]['vary'][0])) {
 210                  $entry[1]['vary'] = array('');
 211              }
 212   
 213              if ($vary != $entry[1]['vary'][0] || !$this->requestsMatch($vary, $entry[0], $storedEnv)) {
 214                  $entries[] = $entry;
 215              }
 216          }
 217   
 218          $headers = $this->persistResponse($response);
 219          unset($headers['age']);
 220   
 221          array_unshift($entries, array($storedEnv, $headers));
 222   
 223          if (false === $this->save($key, serialize($entries))) {
 224              throw new \RuntimeException('Unable to store the metadata.');
 225          }
 226   
 227          return $key;
 228      }
 229   
 230      /**
 231       * Returns content digest for $response.
 232       *
 233       * @param Response $response
 234       *
 235       * @return string
 236       */
 237      protected function generateContentDigest(Response $response)
 238      {
 239          return 'en'.hash('sha256', $response->getContent());
 240      }
 241   
 242      /**
 243       * Invalidates all cache entries that match the request.
 244       *
 245       * @param Request $request A Request instance
 246       *
 247       * @throws \RuntimeException
 248       */
 249      public function invalidate(Request $request)
 250      {
 251          $modified = false;
 252          $key = $this->getCacheKey($request);
 253   
 254          $entries = array();
 255          foreach ($this->getMetadata($key) as $entry) {
 256              $response = $this->restoreResponse($entry[1]);
 257              if ($response->isFresh()) {
 258                  $response->expire();
 259                  $modified = true;
 260                  $entries[] = array($entry[0], $this->persistResponse($response));
 261              } else {
 262                  $entries[] = $entry;
 263              }
 264          }
 265   
 266          if ($modified && false === $this->save($key, serialize($entries))) {
 267              throw new \RuntimeException('Unable to store the metadata.');
 268          }
 269      }
 270   
 271      /**
 272       * Determines whether two Request HTTP header sets are non-varying based on
 273       * the vary response header value provided.
 274       *
 275       * @param string $vary A Response vary header
 276       * @param array  $env1 A Request HTTP header array
 277       * @param array  $env2 A Request HTTP header array
 278       *
 279       * @return bool true if the two environments match, false otherwise
 280       */
 281      private function requestsMatch($vary, $env1, $env2)
 282      {
 283          if (empty($vary)) {
 284              return true;
 285          }
 286   
 287          foreach (preg_split('/[\s,]+/', $vary) as $header) {
 288              $key = str_replace('_', '-', strtolower($header));
 289              $v1 = isset($env1[$key]) ? $env1[$key] : null;
 290              $v2 = isset($env2[$key]) ? $env2[$key] : null;
 291              if ($v1 !== $v2) {
 292                  return false;
 293              }
 294          }
 295   
 296          return true;
 297      }
 298   
 299      /**
 300       * Gets all data associated with the given key.
 301       *
 302       * Use this method only if you know what you are doing.
 303       *
 304       * @param string $key The store key
 305       *
 306       * @return array An array of data associated with the key
 307       */
 308      private function getMetadata($key)
 309      {
 310          if (!$entries = $this->load($key)) {
 311              return array();
 312          }
 313   
 314          return unserialize($entries);
 315      }
 316   
 317      /**
 318       * Purges data for the given URL.
 319       *
 320       * @param string $url A URL
 321       *
 322       * @return bool true if the URL exists and has been purged, false otherwise
 323       */
 324      public function purge($url)
 325      {
 326          $key = $this->getCacheKey(Request::create($url));
 327   
 328          if (isset($this->locks[$key])) {
 329              flock($this->locks[$key], LOCK_UN);
 330              fclose($this->locks[$key]);
 331              unset($this->locks[$key]);
 332          }
 333   
 334          if (file_exists($path = $this->getPath($key))) {
 335              unlink($path);
 336   
 337              return true;
 338          }
 339   
 340          return false;
 341      }
 342   
 343      /**
 344       * Loads data for the given key.
 345       *
 346       * @param string $key The store key
 347       *
 348       * @return string The data associated with the key
 349       */
 350      private function load($key)
 351      {
 352          $path = $this->getPath($key);
 353   
 354          return file_exists($path) ? file_get_contents($path) : false;
 355      }
 356   
 357      /**
 358       * Save data for the given key.
 359       *
 360       * @param string $key  The store key
 361       * @param string $data The data to store
 362       *
 363       * @return bool
 364       */
 365      private function save($key, $data)
 366      {
 367          $path = $this->getPath($key);
 368   
 369          if (isset($this->locks[$key])) {
 370              $fp = $this->locks[$key];
 371              @ftruncate($fp, 0);
 372              @fseek($fp, 0);
 373              $len = @fwrite($fp, $data);
 374              if (strlen($data) !== $len) {
 375                  @ftruncate($fp, 0);
 376   
 377                  return false;
 378              }
 379          } else {
 380              if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
 381                  return false;
 382              }
 383   
 384              $tmpFile = tempnam(dirname($path), basename($path));
 385              if (false === $fp = @fopen($tmpFile, 'wb')) {
 386                  return false;
 387              }
 388              @fwrite($fp, $data);
 389              @fclose($fp);
 390   
 391              if ($data != file_get_contents($tmpFile)) {
 392                  return false;
 393              }
 394   
 395              if (false === @rename($tmpFile, $path)) {
 396                  return false;
 397              }
 398          }
 399   
 400          @chmod($path, 0666 & ~umask());
 401      }
 402   
 403      public function getPath($key)
 404      {
 405          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);
 406      }
 407   
 408      /**
 409       * Generates a cache key for the given Request.
 410       *
 411       * This method should return a key that must only depend on a
 412       * normalized version of the request URI.
 413       *
 414       * If the same URI can have more than one representation, based on some
 415       * headers, use a Vary header to indicate them, and each representation will
 416       * be stored independently under the same cache key.
 417       *
 418       * @param Request $request A Request instance
 419       *
 420       * @return string A key for the given Request
 421       */
 422      protected function generateCacheKey(Request $request)
 423      {
 424          return 'md'.hash('sha256', $request->getUri());
 425      }
 426   
 427      /**
 428       * Returns a cache key for the given Request.
 429       *
 430       * @param Request $request A Request instance
 431       *
 432       * @return string A key for the given Request
 433       */
 434      private function getCacheKey(Request $request)
 435      {
 436          if (isset($this->keyCache[$request])) {
 437              return $this->keyCache[$request];
 438          }
 439   
 440          return $this->keyCache[$request] = $this->generateCacheKey($request);
 441      }
 442   
 443      /**
 444       * Persists the Request HTTP headers.
 445       *
 446       * @param Request $request A Request instance
 447       *
 448       * @return array An array of HTTP headers
 449       */
 450      private function persistRequest(Request $request)
 451      {
 452          return $request->headers->all();
 453      }
 454   
 455      /**
 456       * Persists the Response HTTP headers.
 457       *
 458       * @param Response $response A Response instance
 459       *
 460       * @return array An array of HTTP headers
 461       */
 462      private function persistResponse(Response $response)
 463      {
 464          $headers = $response->headers->all();
 465          $headers['X-Status'] = array($response->getStatusCode());
 466   
 467          return $headers;
 468      }
 469   
 470      /**
 471       * Restores a Response from the HTTP headers and body.
 472       *
 473       * @param array  $headers An array of HTTP headers for the Response
 474       * @param string $body    The Response body
 475       *
 476       * @return Response
 477       */
 478      private function restoreResponse($headers, $body = null)
 479      {
 480          $status = $headers['X-Status'][0];
 481          unset($headers['X-Status']);
 482   
 483          if (null !== $body) {
 484              $headers['X-Body-File'] = array($body);
 485          }
 486   
 487          return new Response($body, $status, $headers);
 488      }
 489  }
 490