From 5068f8751a52ef9d0ce53a7eab21f8bf9a57b940 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 5 Mar 2016 17:35:55 +0100 Subject: [PATCH] [Cache] Add namespace handling to all adapters --- .../Cache/Adapter/AbstractAdapter.php | 6 +-- .../Cache/Adapter/DoctrineAdapter.php | 4 +- .../Cache/Adapter/FilesystemAdapter.php | 8 +++- .../Component/Cache/Adapter/ProxyAdapter.php | 48 ++++++++++++++++--- .../Cache/Tests/Adapter/ApcuAdapterTest.php | 2 +- .../Adapter/NamespacedProxyAdapterTest.php | 26 ++++++++++ 6 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index dbb8258936..a6da1952a5 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -32,7 +32,7 @@ abstract class AbstractAdapter implements CacheItemPoolInterface, LoggerAwareInt protected function __construct($namespace = '', $defaultLifetime = 0) { - $this->namespace = $namespace; + $this->namespace = $this->getId($namespace, true); $this->createCacheItem = \Closure::bind( function ($key, $value, $isHit) use ($defaultLifetime) { $item = new CacheItem(); @@ -331,12 +331,12 @@ abstract class AbstractAdapter implements CacheItemPoolInterface, LoggerAwareInt } } - private function getId($key) + private function getId($key, $ns = false) { if (!is_string($key)) { throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', is_object($key) ? get_class($key) : gettype($key))); } - if (!isset($key[0])) { + if (!isset($key[0]) && !$ns) { throw new InvalidArgumentException('Cache key length must be greater than zero'); } if (isset($key[strcspn($key, '{}()/\@:')])) { diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php index 41da56d597..f89148a787 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php @@ -20,9 +20,9 @@ class DoctrineAdapter extends AbstractAdapter { private $provider; - public function __construct(CacheProvider $provider, $defaultLifetime = null) + public function __construct(CacheProvider $provider, $defaultLifetime = 0, $namespace = '') { - parent::__construct('', $defaultLifetime); + parent::__construct($namespace, $defaultLifetime); $this->provider = $provider; } diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index db6d9ce4cf..a9ac89ff2a 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -20,10 +20,16 @@ class FilesystemAdapter extends AbstractAdapter { private $directory; - public function __construct($directory, $defaultLifetime = null) + public function __construct($directory, $defaultLifetime = 0, $namespace = '') { parent::__construct('', $defaultLifetime); + if (!isset($directory[0])) { + $directory = sys_get_temp_dir().'/symfony-cache'; + } + if (isset($namespace[0])) { + $directory .= '/'.$namespace; + } if (!file_exists($dir = $directory.'/.')) { @mkdir($directory, 0777, true); } diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index eaeb5ba4fb..3bce3cb5bb 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Nicolas Grekas @@ -21,19 +22,24 @@ use Symfony\Component\Cache\CacheItem; class ProxyAdapter implements CacheItemPoolInterface { private $pool; + private $namespace; + private $namespaceLen; private $createCacheItem; private $hits = 0; private $misses = 0; - public function __construct(CacheItemPoolInterface $pool) + public function __construct(CacheItemPoolInterface $pool, $defaultLifetime = 0, $namespace = '') { $this->pool = $pool; + $this->namespace = $this->getId($namespace, true); + $this->namespaceLen = strlen($namespace); $this->createCacheItem = \Closure::bind( - function ($key, $value, $isHit) { + function ($key, $value, $isHit) use ($defaultLifetime) { $item = new CacheItem(); $item->key = $key; $item->value = $value; $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; return $item; }, @@ -48,7 +54,7 @@ class ProxyAdapter implements CacheItemPoolInterface public function getItem($key) { $f = $this->createCacheItem; - $item = $this->pool->getItem($key); + $item = $this->pool->getItem($this->getId($key)); if ($isHit = $item->isHit()) { ++$this->hits; } else { @@ -63,6 +69,12 @@ class ProxyAdapter implements CacheItemPoolInterface */ public function getItems(array $keys = array()) { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + return $this->generateItems($this->pool->getItems($keys)); } @@ -71,7 +83,7 @@ class ProxyAdapter implements CacheItemPoolInterface */ public function hasItem($key) { - return $this->pool->hasItem($key); + return $this->pool->hasItem($this->getId($key)); } /** @@ -87,7 +99,7 @@ class ProxyAdapter implements CacheItemPoolInterface */ public function deleteItem($key) { - return $this->pool->deleteItem($key); + return $this->pool->deleteItem($this->getId($key)); } /** @@ -95,6 +107,12 @@ class ProxyAdapter implements CacheItemPoolInterface */ public function deleteItems(array $keys) { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + return $this->pool->deleteItems($keys); } @@ -129,7 +147,7 @@ class ProxyAdapter implements CacheItemPoolInterface } $item = (array) $item; $expiry = $item[CacheItem::CAST_PREFIX.'expiry']; - $poolItem = $this->pool->getItem($item[CacheItem::CAST_PREFIX.'key']); + $poolItem = $this->pool->getItem($this->namespace.$item[CacheItem::CAST_PREFIX.'key']); $poolItem->set($item[CacheItem::CAST_PREFIX.'value']); $poolItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); @@ -146,6 +164,9 @@ class ProxyAdapter implements CacheItemPoolInterface } else { ++$this->misses; } + if ($this->namespaceLen) { + $key = substr($key, $this->namespaceLen); + } yield $key => $f($key, $item->get(), $isHit); } @@ -170,4 +191,19 @@ class ProxyAdapter implements CacheItemPoolInterface { return $this->misses; } + + private function getId($key, $ns = false) + { + if (!is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', is_object($key) ? get_class($key) : gettype($key))); + } + if (!isset($key[0]) && !$ns) { + throw new InvalidArgumentException('Cache key length must be greater than zero'); + } + if (isset($key[strcspn($key, '{}()/\@:')])) { + throw new InvalidArgumentException('Cache key contains reserved characters {}()/\@:'); + } + + return $this->namespace.$key; + } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php index aa2b170f4b..b6ce21e1ef 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php @@ -28,6 +28,6 @@ class ApcuAdapterTest extends CachePoolTest $this->markTestSkipped('Fails transiently on Windows.'); } - return new ApcuAdapter(__CLASS__); + return new ApcuAdapter(str_replace('\\', '.', __CLASS__)); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php new file mode 100644 index 0000000000..28898e780e --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; + +/** + * @group time-sensitive + */ +class NamespacedProxyAdapterTest extends ProxyAdapterTest +{ + public function createCachePool() + { + return new ProxyAdapter(new ArrayAdapter(), 0, 'foo'); + } +}