-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[WIP][HttpKernel] PSR6 Store for HttpCache #20061
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 |
---|---|---|
@@ -0,0 +1,242 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\HttpKernel\HttpCache; | ||
|
||
use Psr\Cache\CacheItemPoolInterface; | ||
use Symfony\Component\Cache\CacheItem; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
final class Psr6Store implements StoreInterface | ||
{ | ||
/** | ||
* @var CacheItemPoolInterface | ||
*/ | ||
private $cachePool; | ||
|
||
/** | ||
* List of locks acquired by the current process. | ||
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. not sure this adds value |
||
* | ||
* @var array | ||
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. nor that |
||
*/ | ||
private $locks = array(); | ||
|
||
/** | ||
* @param CacheItemPoolInterface $cachePool | ||
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. already in the constructor |
||
*/ | ||
public function __construct(CacheItemPoolInterface $cachePool) | ||
{ | ||
$this->cachePool = $cachePool; | ||
} | ||
|
||
/** | ||
* Locates a cached Response for the Request provided. | ||
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.
|
||
* | ||
* @param Request $request A Request instance | ||
* | ||
* @return Response|null A Response instance, or null if no cache entry was found | ||
*/ | ||
public function lookup(Request $request) | ||
{ | ||
// TODO: Implement lookup() method. | ||
} | ||
|
||
/** | ||
* Writes a cache entry to the store for the given Request and Response. | ||
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.
|
||
* | ||
* Existing entries are read and any that match the response are removed. This | ||
* method calls write with the new list of cache entries. | ||
* | ||
* @param Request $request A Request instance | ||
* @param Response $response A Response instance | ||
* | ||
* @return string The key under which the response is stored | ||
*/ | ||
public function write(Request $request, Response $response) | ||
{ | ||
if (!$response->headers->has('X-Content-Digest')) { | ||
$contentDigest = $this->generateContentDigest($response); | ||
|
||
if (false === $this->save($contentDigest, $response->getContent())) { | ||
throw new \RuntimeException('Unable to store the entity.'); | ||
} | ||
|
||
$response->headers->set('X-Content-Digest', $contentDigest); | ||
|
||
if (!$response->headers->has('Transfer-Encoding')) { | ||
$response->headers->set('Content-Length', strlen($response->getContent())); | ||
} | ||
} | ||
|
||
$key = $this->getCacheKey($request); | ||
$headers = $response->headers->all(); | ||
unset($headers['age']); | ||
|
||
$this->save($key, serialize(array(array($request->headers->all(), $headers)))); | ||
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. serializing is already done by the cache pool |
||
} | ||
|
||
/** | ||
* Invalidates all cache entries that match the request. | ||
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.
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. (same below :) ) |
||
* | ||
* @param Request $request A Request instance | ||
*/ | ||
public function invalidate(Request $request) | ||
{ | ||
// TODO: Implement invalidate() method. | ||
} | ||
|
||
/** | ||
* Locks the cache for a given Request. | ||
* | ||
* @param Request $request A Request instance | ||
* | ||
* @return bool|string true if the lock is acquired, the path to the current lock otherwise | ||
*/ | ||
public function lock(Request $request) | ||
{ | ||
$lockKey = $this->getLockKey($request); | ||
|
||
if (isset($this->locks[$lockKey])) { | ||
return true; | ||
} | ||
|
||
$item = $this->cachePool->getItem($lockKey); | ||
|
||
if ($item->isHit()) { | ||
return false; | ||
} | ||
|
||
$this->cachePool->save($item); | ||
|
||
$this->locks[$lockKey] = true; | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Releases the lock for the given Request. | ||
* | ||
* @param Request $request A Request instance | ||
* | ||
* @return bool False if the lock file does not exist or cannot be unlocked, true otherwise | ||
*/ | ||
public function unlock(Request $request) | ||
{ | ||
$lockKey = $this->getLockKey($request); | ||
|
||
if (!isset($this->locks[$lockKey])) { | ||
return false; | ||
} | ||
|
||
$this->cachePool->deleteItem($lockKey); | ||
|
||
unset($this->locks[$lockKey]); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Returns whether or not a lock exists. | ||
* | ||
* @param Request $request A Request instance | ||
* | ||
* @return bool true if lock exists, false otherwise | ||
*/ | ||
public function isLocked(Request $request) | ||
{ | ||
$lockKey = $this->getLockKey($request); | ||
|
||
if (isset($this->locks[$lockKey])) { | ||
return true; | ||
} | ||
|
||
return $this->cachePool->hasItem($this->getLockKey($request)); | ||
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. use |
||
} | ||
|
||
/** | ||
* Purges data for the given URL. | ||
* | ||
* @param string $url A URL | ||
* | ||
* @return bool true if the URL exists and has been purged, false otherwise | ||
*/ | ||
public function purge($url) | ||
{ | ||
// TODO: Implement purge() method. | ||
} | ||
|
||
/** | ||
* Cleanups storage. | ||
*/ | ||
public function cleanup() | ||
{ | ||
$this->cachePool->deleteItems(array_keys($this->locks)); | ||
$this->locks = array(); | ||
} | ||
|
||
/** | ||
* @param Request $request | ||
* | ||
* @return string | ||
*/ | ||
private function getCacheKey(Request $request) | ||
{ | ||
return 'md'.hash('sha256', $request->getUri()); | ||
} | ||
|
||
/** | ||
* @param Request $request | ||
* | ||
* @return string | ||
*/ | ||
private function getLockKey(Request $request) | ||
{ | ||
return $this->getCacheKey($request).'.lock'; | ||
} | ||
|
||
/** | ||
* @param Response $response | ||
* | ||
* @return string | ||
*/ | ||
private function generateContentDigest(Response $response) | ||
{ | ||
return 'en'.hash('sha256', $response->getContent()); | ||
} | ||
|
||
/** | ||
* @param string $key | ||
* @param string $data | ||
* | ||
* @return bool | ||
*/ | ||
private function save($key, $data) | ||
{ | ||
return $this->cachePool->save($this->createCacheItem($key, $data)); | ||
} | ||
|
||
/** | ||
* @param string $key | ||
* @param mixed $value | ||
* @param bool $isHit | ||
* | ||
* @return CacheItem | ||
*/ | ||
private function createCacheItem($key, $value, $isHit = false) | ||
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. This makes the implementation work only with Symfony's Cache components: cache items aren't interoperable. 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. This is kind of feedback i wanted to get (ignore comments etc, i left them there for my reference until I'm done and replace them with inheritdoc) ;)
I did it first, perhaps I tried to optimise too early and replaced a call to pool with this. I'll revert back. Once it's all working we can run tests and see if it needs improving. 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. Having the optimization is OK if you keep the non-optimized version for other pools |
||
{ | ||
$f = \Closure::bind( | ||
function ($key, $value, $isHit) { | ||
$item = new CacheItem(); | ||
$item->key = $key; | ||
$item->value = $value; | ||
$item->isHit = $isHit; | ||
|
||
return $item; | ||
}, | ||
null, | ||
CacheItem::class | ||
); | ||
|
||
return $f($key, $value, $isHit); | ||
} | ||
} |
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.
not needed, already hinted in the constructor