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