-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[2.3] [WIP][HttpCache] StoreHouseKeepingInteface #6855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,17 +16,19 @@ | |
|
||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Finder\Finder; | ||
|
||
/** | ||
* Store implements all the logic for storing cache metadata (Request and Response headers). | ||
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
class Store implements StoreInterface | ||
class Store implements StoreInterface, StoreHousekeepingInterface | ||
{ | ||
protected $root; | ||
private $keyCache; | ||
private $locks; | ||
private $responseContentList; | ||
|
||
/** | ||
* Constructor. | ||
|
@@ -41,6 +43,107 @@ public function __construct($root) | |
} | ||
$this->keyCache = new \SplObjectStorage(); | ||
$this->locks = array(); | ||
$this->responseContentList = array(); | ||
} | ||
|
||
/** | ||
* Deletes from disk all the stale response-header files and the response-content files. | ||
* | ||
* @return integer The number of the cleared entries | ||
*/ | ||
public function clear() | ||
{ | ||
$finder = new Finder(); | ||
$finder->depth('< 5')->files()->notName('*.lck')->in($this->root . DIRECTORY_SEPARATOR . 'md'); | ||
$counter = 0; | ||
foreach ($finder as $file) { | ||
$counter += $this->purgeKey($this->getKeyByPath($file->getPathname())); | ||
} | ||
foreach ($this->getResponseContentList() as $key => $isNeeded) { | ||
if (!$isNeeded) { | ||
$file = $this->getPath($key); | ||
if ($this->deleteFile($file)) { | ||
$counter++; | ||
} | ||
} | ||
} | ||
|
||
return $counter; | ||
} | ||
|
||
/** | ||
* When the Response is stale it deletes the header file and add its response content file into the black list. | ||
* | ||
* @param string $key The cached key | ||
* | ||
* @return integer the number of file deleted | ||
*/ | ||
protected function purgeKey($key) | ||
{ | ||
$metadata = $this->getMetadata($key); | ||
$expiredResponse = 0; | ||
$contentDeleted = 0; | ||
foreach ($metadata as $entry) { | ||
$response = $this->restoreResponse($entry[1]); | ||
$ResponseContentKey = $response->headers->get('x-content-digest'); | ||
if (!$response->isFresh()) { | ||
$this->addToResponseContentList($ResponseContentKey, false); | ||
$expiredResponse++; | ||
} else { | ||
$this->addToResponseContentList($ResponseContentKey, true); | ||
} | ||
} | ||
// removes the file, only if all entries are expired | ||
if (count($metadata) == $expiredResponse) { | ||
$file = $this->getPath($key); | ||
if ($this->deleteFile($file, false)) { | ||
$contentDeleted++; | ||
} | ||
// deletes locks | ||
$this->deleteFile($this->getPath($key . '.lck'), false); | ||
} | ||
|
||
return $contentDeleted; | ||
} | ||
|
||
/** | ||
* Add a key of a response in the ResponseContentList. | ||
* | ||
* @param string $key | ||
* @param Boolean $need | ||
* | ||
* @return Store | ||
*/ | ||
private function addToResponseContentList($key, $need = true) | ||
{ | ||
if (array_key_exists($key, $this->responseContentList)) { | ||
$this->responseContentList[$key] = $this->responseContentList[$key] || $need; | ||
} else { | ||
$this->responseContentList[$key] = $need; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Deletes a file. | ||
* | ||
* @param string $file | ||
* @param Boolean $ignoreError | ||
* | ||
* @return Boolean | ||
*/ | ||
protected function deleteFile($file, $ignoreError = true) | ||
{ | ||
if (is_file($file)) { | ||
if ($ignoreError) { | ||
return @unlink($file); | ||
} | ||
|
||
return unlink($file); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
|
@@ -99,7 +202,7 @@ public function unlock(Request $request) | |
{ | ||
$file = $this->getPath($this->getCacheKey($request).'.lck'); | ||
|
||
return is_file($file) ? @unlink($file) : false; | ||
return $this->deleteFile($file); | ||
} | ||
|
||
public function isLocked(Request $request) | ||
|
@@ -368,11 +471,42 @@ private function save($key, $data) | |
@chmod($path, 0666 & ~umask()); | ||
} | ||
|
||
/** | ||
* Get the Path given the cache-key. | ||
* | ||
* @param string $key | ||
* | ||
* @return string | ||
*/ | ||
public function getPath($key) | ||
{ | ||
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); | ||
} | ||
|
||
/** | ||
* Get the cache key given the path. | ||
* | ||
* @param string $path The absolute path | ||
* | ||
* @return string | ||
*/ | ||
public function getKeyByPath($path) | ||
{ | ||
$path = substr($path, strlen($this->root.DIRECTORY_SEPARATOR)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should probably normalize the path (D_S) as the method is public There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is that if we use |
||
|
||
return str_replace(DIRECTORY_SEPARATOR, '', $path); | ||
} | ||
|
||
/** | ||
* Get the responseContentList. | ||
* | ||
* @return array | ||
*/ | ||
public function getResponseContentList() | ||
{ | ||
return $this->responseContentList; | ||
} | ||
|
||
/** | ||
* Returns a cache key for the given Request. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpKernel\HttpCache; | ||
|
||
/** | ||
* Interface implemented by HTTP cache stores, the stores should provide a cleaner | ||
* | ||
* @author Giulio De Donato <liuggio@gmail.com> | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
interface StoreHousekeepingInterface | ||
{ | ||
/** | ||
* clear all the stale entries | ||
* | ||
* @return int the number of the cleared entries | ||
*/ | ||
public function clear(); | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the finder looks problematic to me, it'd become very complicated to switch to the cache component or implement this in a custom store, the input data comes from a trick only possible with filesystem AFAIK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, what do you suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually here it'd be overkill, as it's an implementation relying on filesystem, there is no reason not to leverage an existing tool helping out.
I was just pointing out the fact that if the plan is to make HttpCache use the yet to come Cache component for 2.3, most of this PR would probably have to be rewritten anyway.