[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |