diff --git a/library/config.php b/library/config.php index 665abe5b5..d0ad2eedc 100644 --- a/library/config.php +++ b/library/config.php @@ -24,6 +24,7 @@ $config = [ 'adapter' => \TorrentPier\Cache\FileAdapter::class, 'options' => [ 'directory' => __DIR__ . '/../internal_data/cache', + 'prefix' => 'hash string' ], ], /*'cache' => [ diff --git a/src/Cache/Adapter.php b/src/Cache/Adapter.php index f9aa78b9c..850e70838 100644 --- a/src/Cache/Adapter.php +++ b/src/Cache/Adapter.php @@ -17,6 +17,11 @@ abstract class Adapter */ protected $provider; + /** + * @var string + */ + protected $prefix = ''; + /** * Set options for setting cache provider. * @@ -48,6 +53,18 @@ abstract class Adapter */ abstract protected function getType(); + /** + * Add prefix to the namespace for security of key. + * + * @param string $prefix + * @return $this + */ + protected function setPrefix($prefix) + { + $this->prefix = $prefix; + return $this; + } + /** * Prepare key. * @@ -68,7 +85,7 @@ abstract class Adapter list($namespace, $id) = $result; } - $this->getProvider()->setNamespace($namespace); + $this->getProvider()->setNamespace($this->prefix . $namespace); return $id; } @@ -89,7 +106,7 @@ abstract class Adapter * Fetches an entry from the cache. * * @param string $key - * @param mixed|null $defaultValue + * @param mixed|\Closure|null $defaultValue * @return mixed|null */ public function get($key, $defaultValue = null) @@ -98,6 +115,10 @@ abstract class Adapter $result = $this->getProvider()->fetch($id); if ($result === false) { + if ($defaultValue instanceof \Closure) { + return call_user_func($defaultValue, $this, $key); + } + return $defaultValue; } @@ -108,12 +129,13 @@ abstract class Adapter * Returns an associative array of values for keys is found in cache. * * @param array $keys + * @param string $namespace * @return array|\string[] */ - public function gets(array $keys) + public function gets(array $keys, $namespace = self::DEFAULT_NAMESPACE) { - $keys = array_map([$this, 'prepareKey'], $keys); - return $this->getProvider()->fetchMultiple([$keys]); + $this->getProvider()->setNamespace($this->prefix . $namespace); + return $this->getProvider()->fetchMultiple($keys); } /** @@ -134,16 +156,13 @@ abstract class Adapter * Returns a boolean value indicating if the operation succeeded. * * @param array $keysAndValues + * @param string $namespace * @param int $timeLeft * @return bool */ - public function sets(array $keysAndValues, $timeLeft) + public function sets(array $keysAndValues, $namespace = self::DEFAULT_NAMESPACE, $timeLeft = 0) { - foreach ($keysAndValues as $key => $value) { - $id = $this->prepareKey($key); - $keysAndValues[$id] = $value; - unset($keysAndValues[$key]); - } + $this->getProvider()->setNamespace($this->prefix . $namespace); return $this->getProvider()->saveMultiple($keysAndValues, $timeLeft); } @@ -167,7 +186,7 @@ abstract class Adapter */ public function deleteAll($namespace = '') { - $this->getProvider()->setNamespace($namespace); + $this->getProvider()->setNamespace($this->prefix . $namespace); return $this->getProvider()->deleteAll(); } diff --git a/src/Cache/FileAdapter.php b/src/Cache/FileAdapter.php index ecdf352c1..43e7cdefa 100644 --- a/src/Cache/FileAdapter.php +++ b/src/Cache/FileAdapter.php @@ -2,7 +2,7 @@ namespace TorrentPier\Cache; -use Doctrine\Common\Cache\PhpFileCache; +use Doctrine\Common\Cache\FilesystemCache; /** * Class FileAdapter @@ -18,7 +18,7 @@ class FileAdapter extends Adapter /** * @var string The cache file extension. */ - protected $extension = PhpFileCache::EXTENSION; + protected $extension = FilesystemCache::EXTENSION; /** * @var int The cache file umask. @@ -31,7 +31,7 @@ class FileAdapter extends Adapter public function getProvider() { if (!$this->provider) { - $this->provider = new PhpFileCache($this->directory, $this->extension, $this->umask); + $this->provider = new FilesystemCache($this->directory, $this->extension, $this->umask); } return $this->provider; @@ -42,7 +42,7 @@ class FileAdapter extends Adapter */ protected function getType() { - return 'Cache in php file'; + return 'Filesystem Cache'; } /** diff --git a/src/Cache/MemoryAdapter.php b/src/Cache/MemoryAdapter.php index c9ce5c4f5..f9fed2557 100644 --- a/src/Cache/MemoryAdapter.php +++ b/src/Cache/MemoryAdapter.php @@ -98,6 +98,6 @@ class MemoryAdapter extends Adapter */ protected function getType() { - return $this->isMemcached ? 'Cache in memcached' : 'Cache in memcache'; + return sprintf('Memory Cache (Driver: %s)', $this->isMemcached ? 'memcached' : 'memcache'); } } diff --git a/tests/src/Cache/AdapterTest.php b/tests/src/Cache/AdapterTest.php new file mode 100644 index 000000000..5fbdcc7cc --- /dev/null +++ b/tests/src/Cache/AdapterTest.php @@ -0,0 +1,154 @@ +cacheProvider = $this->getMock(CacheProvider::class, + ['doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush', 'doGetStats']); + + $this->adapter = $this->getMock(Adapter::class, ['getProvider', 'getType']); + $this->adapter->method('getProvider')->willReturn($this->cacheProvider); + $this->adapter->method('getType')->willReturn('Void Cache for tests'); + } + + /** + * @covers TorrentPier\Cache\Adapter::has + */ + public function testHas() + { + $this->cacheProvider->expects(self::once())->method('doContains') + ->with('namespaceTest[keyTest][1]')->willReturn(false); + + self::assertEquals(false, $this->adapter->has('namespaceTest::keyTest')); + } + + /** + * @covers TorrentPier\Cache\Adapter::get + */ + public function testGet() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doFetch') + ->with('namespaceTest[keyTest][1]')->willReturn(false); + + self::assertEquals(false, $this->adapter->get('namespaceTest::keyTest')); + } + + /** + * @covers TorrentPier\Cache\Adapter::get + */ + public function testGetCallback() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doFetch') + ->with('namespaceTest[keyTest][1]')->willReturn(false); + + $callback = function($cache, $key) { + return [$cache instanceof Adapter, $key]; + }; + + self::assertEquals([true, 'namespaceTest::keyTest'], $this->adapter->get('namespaceTest::keyTest', $callback)); + } + + /** + * @covers TorrentPier\Cache\Adapter::set + */ + public function testSet() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doSave') + ->with('namespaceTest[keyTest][1]', [1,2,3,4], 10)->willReturn(true); + + self::assertEquals(true, $this->adapter->set('namespaceTest::keyTest', [1,2,3,4], 10)); + } + + public function testGets() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doFetch') + ->with('namespaceTest[keyTest][1]')->willReturn('testValue'); + + self::assertEquals(['keyTest' => 'testValue'], $this->adapter->gets(['keyTest'], 'namespaceTest')); + } + + public function testSets() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doSave') + ->with('namespaceTest[keyTest][1]', [1,2,3,4], 10)->willReturn(true); + + self::assertEquals(true, $this->adapter->sets(['keyTest' => [1,2,3,4]], 'namespaceTest', 10)); + } + + /** + * @covers TorrentPier\Cache\Adapter::delete + */ + public function testDelete() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doDelete') + ->with('namespaceTest[keyTest][1]')->willReturn(true); + + self::assertEquals(true, $this->adapter->delete('namespaceTest::keyTest')); + } + + /** + * @covers TorrentPier\Cache\Adapter::deleteAll + */ + public function testDeleteAll() + { + $this->cacheProvider->expects(self::at(0))->method('doFetch') + ->with('DoctrineNamespaceCacheKey[namespaceTest]')->willReturn(false); + + $this->cacheProvider->expects(self::at(1))->method('doSave') + ->with('DoctrineNamespaceCacheKey[namespaceTest]', 2)->willReturn(true); + + self::assertEquals(true, $this->adapter->deleteAll('namespaceTest')); + } + + /** + * @covers TorrentPier\Cache\Adapter::flush + */ + public function testFlush() + { + $this->cacheProvider->expects(self::at(0))->method('doFlush')->willReturn(true); + + self::assertEquals(true, $this->adapter->flush()); + } + + /** + * @covers TorrentPier\Cache\Adapter::stats + */ + public function testStats() + { + $this->cacheProvider->expects(self::at(0))->method('doGetStats')->willReturn(null); + + self::assertEquals(['type' => 'Void Cache for tests'], $this->adapter->stats()); + } +} diff --git a/tests/src/Cache/FileAdapterTest.php b/tests/src/Cache/FileAdapterTest.php index 0e41e29d1..5fe9827ae 100644 --- a/tests/src/Cache/FileAdapterTest.php +++ b/tests/src/Cache/FileAdapterTest.php @@ -2,7 +2,7 @@ namespace TorrentPier\Cache; -use Doctrine\Common\Cache\PhpFileCache; +use Doctrine\Common\Cache\FilesystemCache; /** * Class FileAdapterTest @@ -31,7 +31,7 @@ class FileAdapterTest extends \PHPUnit_Framework_TestCase */ public function testAdapterCache() { - self::assertInstanceOf(PhpFileCache::class, $this->adapter->getProvider()); + self::assertInstanceOf(FilesystemCache::class, $this->adapter->getProvider()); } /** @@ -39,7 +39,7 @@ class FileAdapterTest extends \PHPUnit_Framework_TestCase */ public function testStatsType() { - self::assertEquals('Cache in php file', $this->adapter->stats()['type']); + self::assertEquals('Filesystem Cache', $this->adapter->stats()['type']); } /**