[Cache] Added PhpFilesAdapter
This commit is contained in:
parent
c71868d06f
commit
14bcd799c7
@ -11,43 +11,27 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Cache\Adapter;
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
use Symfony\Component\Cache\Adapter\Helper\FilesCacheHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
*/
|
*/
|
||||||
class FilesystemAdapter extends AbstractAdapter
|
class FilesystemAdapter extends AbstractAdapter
|
||||||
{
|
{
|
||||||
private $directory;
|
/**
|
||||||
|
* @var FilesCacheHelper
|
||||||
|
*/
|
||||||
|
protected $filesCacheHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $namespace Cache namespace
|
||||||
|
* @param int $defaultLifetime Default lifetime for cache items
|
||||||
|
* @param null $directory Path where cache items should be stored, defaults to sys_get_temp_dir().'/symfony-cache'
|
||||||
|
*/
|
||||||
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
|
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
|
||||||
{
|
{
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
$this->filesCacheHelper = new FilesCacheHelper($directory, $namespace);
|
||||||
if (!isset($directory[0])) {
|
|
||||||
$directory = sys_get_temp_dir().'/symfony-cache';
|
|
||||||
}
|
|
||||||
if (isset($namespace[0])) {
|
|
||||||
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('FilesystemAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
|
||||||
}
|
|
||||||
$directory .= '/'.$namespace;
|
|
||||||
}
|
|
||||||
if (!file_exists($dir = $directory.'/.')) {
|
|
||||||
@mkdir($directory, 0777, true);
|
|
||||||
}
|
|
||||||
if (false === $dir = realpath($dir)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
|
|
||||||
}
|
|
||||||
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
|
|
||||||
}
|
|
||||||
// On Windows the whole path is limited to 258 chars
|
|
||||||
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) > 234) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->directory = $dir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +43,7 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$file = $this->getFile($id);
|
$file = $this->filesCacheHelper->getFilePath($id);
|
||||||
if (!$h = @fopen($file, 'rb')) {
|
if (!$h = @fopen($file, 'rb')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -86,7 +70,7 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
*/
|
*/
|
||||||
protected function doHave($id)
|
protected function doHave($id)
|
||||||
{
|
{
|
||||||
$file = $this->getFile($id);
|
$file = $this->filesCacheHelper->getFilePath($id);
|
||||||
|
|
||||||
return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id)));
|
return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id)));
|
||||||
}
|
}
|
||||||
@ -97,8 +81,9 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
protected function doClear($namespace)
|
protected function doClear($namespace)
|
||||||
{
|
{
|
||||||
$ok = true;
|
$ok = true;
|
||||||
|
$directory = $this->filesCacheHelper->getDirectory();
|
||||||
|
|
||||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
||||||
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
|
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +98,7 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
$ok = true;
|
$ok = true;
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$file = $this->getFile($id);
|
$file = $this->filesCacheHelper->getFilePath($id);
|
||||||
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
|
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,32 +112,24 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
{
|
{
|
||||||
$ok = true;
|
$ok = true;
|
||||||
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
|
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
|
||||||
$tmp = $this->directory.uniqid('', true);
|
|
||||||
|
|
||||||
foreach ($values as $id => $value) {
|
foreach ($values as $id => $value) {
|
||||||
$file = $this->getFile($id, true);
|
$fileContent = $this->createCacheFileContent($id, $value, $expiresAt);
|
||||||
|
$ok = $this->filesCacheHelper->saveFileForId($id, $fileContent, $expiresAt) && $ok;
|
||||||
$value = $expiresAt."\n".rawurlencode($id)."\n".serialize($value);
|
|
||||||
if (false !== @file_put_contents($tmp, $value)) {
|
|
||||||
@touch($tmp, $expiresAt);
|
|
||||||
$ok = @rename($tmp, $file) && $ok;
|
|
||||||
} else {
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getFile($id, $mkdir = false)
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $expiresAt
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function createCacheFileContent($id, $value, $expiresAt)
|
||||||
{
|
{
|
||||||
$hash = str_replace('/', '-', base64_encode(md5($id, true)));
|
return $expiresAt."\n".rawurlencode($id)."\n".serialize($value);
|
||||||
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
|
|
||||||
|
|
||||||
if ($mkdir && !file_exists($dir)) {
|
|
||||||
@mkdir($dir, 0777, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dir.substr($hash, 2, -2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
137
src/Symfony/Component/Cache/Adapter/Helper/FilesCacheHelper.php
Normal file
137
src/Symfony/Component/Cache/Adapter/Helper/FilesCacheHelper.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?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\Cache\Adapter\Helper;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
|
class FilesCacheHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $fileSuffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $directory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $directory Path where cache items should be stored, defaults to sys_get_temp_dir().'/symfony-cache'
|
||||||
|
* @param string $namespace Cache namespace
|
||||||
|
* @param string $version Version (works the same way as namespace)
|
||||||
|
* @param string $fileSuffix Suffix that will be appended to all file names
|
||||||
|
*/
|
||||||
|
public function __construct($directory = null, $namespace = null, $version = null, $fileSuffix = '')
|
||||||
|
{
|
||||||
|
if (!isset($directory[0])) {
|
||||||
|
$directory = sys_get_temp_dir().'/symfony-cache';
|
||||||
|
}
|
||||||
|
if (isset($namespace[0])) {
|
||||||
|
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache namespace for filesystem cache contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
||||||
|
}
|
||||||
|
$directory .= '/'.$namespace;
|
||||||
|
}
|
||||||
|
if (isset($version[0])) {
|
||||||
|
if (preg_match('#[^-+_.A-Za-z0-9]#', $version, $match)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache version contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
||||||
|
}
|
||||||
|
$directory .= '/'.$version;
|
||||||
|
}
|
||||||
|
if (!file_exists($dir = $directory.'/.')) {
|
||||||
|
@mkdir($directory, 0777, true);
|
||||||
|
}
|
||||||
|
if (false === $dir = realpath($dir)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
|
||||||
|
}
|
||||||
|
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
|
||||||
|
}
|
||||||
|
// On Windows the whole path is limited to 258 chars
|
||||||
|
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) + strlen($fileSuffix) > 234) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fileSuffix = $fileSuffix;
|
||||||
|
$this->directory = $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns root cache directory.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDirectory()
|
||||||
|
{
|
||||||
|
return $this->directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves entry in cache.
|
||||||
|
*
|
||||||
|
* @param string $id Id of the cache entry (used for obtaining file path to write to).
|
||||||
|
* @param string $fileContent Content to write to cache file
|
||||||
|
* @param int|null $modificationTime If this is not-null it will be passed to touch()
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveFileForId($id, $fileContent, $modificationTime = null)
|
||||||
|
{
|
||||||
|
$file = $this->getFilePath($id, true);
|
||||||
|
|
||||||
|
return $this->saveFile($file, $fileContent, $modificationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves entry in cache.
|
||||||
|
*
|
||||||
|
* @param string $file File path to cache entry.
|
||||||
|
* @param string $fileContent Content to write to cache file
|
||||||
|
* @param int|null $modificationTime If this is not-null it will be passed to touch()
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveFile($file, $fileContent, $modificationTime = null)
|
||||||
|
{
|
||||||
|
$temporaryFile = $this->directory.uniqid('', true);
|
||||||
|
if (false === @file_put_contents($temporaryFile, $fileContent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $modificationTime) {
|
||||||
|
@touch($temporaryFile, $modificationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return @rename($temporaryFile, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns file path to cache entry.
|
||||||
|
*
|
||||||
|
* @param string $id Cache entry id.
|
||||||
|
* @param bool $mkdir Whether to create necessary directories before returning file path.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFilePath($id, $mkdir = false)
|
||||||
|
{
|
||||||
|
$hash = str_replace('/', '-', base64_encode(md5($id, true)));
|
||||||
|
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
|
if ($mkdir && !file_exists($dir)) {
|
||||||
|
@mkdir($dir, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dir.substr($hash, 2, -2).$this->fileSuffix;
|
||||||
|
}
|
||||||
|
}
|
157
src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
Normal file
157
src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?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\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Adapter\Helper\FilesCacheHelper;
|
||||||
|
|
||||||
|
class PhpFilesAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var FilesCacheHelper
|
||||||
|
*/
|
||||||
|
protected $filesCacheHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $namespace Cache namespace
|
||||||
|
* @param int $defaultLifetime Default lifetime for cache items
|
||||||
|
* @param null $directory Path where cache items should be stored, defaults to sys_get_temp_dir().'/symfony-cache'
|
||||||
|
* @param string $version Version (works the same way as namespace)
|
||||||
|
*/
|
||||||
|
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null, $version = null)
|
||||||
|
{
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
$this->filesCacheHelper = new FilesCacheHelper($directory, $namespace, $version, '.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$values = array();
|
||||||
|
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$valueArray = $this->includeCacheFile($this->filesCacheHelper->getFilePath($id));
|
||||||
|
if (!is_array($valueArray)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$values[$id] = $valueArray[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave($id)
|
||||||
|
{
|
||||||
|
return 0 !== count($this->doFetch(array($id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear($namespace)
|
||||||
|
{
|
||||||
|
$directory = $this->filesCacheHelper->getDirectory();
|
||||||
|
|
||||||
|
return !(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS))->valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$file = $this->filesCacheHelper->getFilePath($id);
|
||||||
|
if (@file_exists($file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, $lifetime)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
|
||||||
|
|
||||||
|
foreach ($values as $id => $value) {
|
||||||
|
$file = $this->filesCacheHelper->getFilePath($id, true);
|
||||||
|
if (file_exists($file)) {
|
||||||
|
$ok = false;
|
||||||
|
} else {
|
||||||
|
$ok = $this->saveCacheFile($file, $value, $expiresAt) && $ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $file
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $expiresAt
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function saveCacheFile($file, $value, $expiresAt)
|
||||||
|
{
|
||||||
|
$fileContent = $this->createCacheFileContent($value, $expiresAt);
|
||||||
|
|
||||||
|
return $this->filesCacheHelper->saveFile($file, $fileContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $file File path
|
||||||
|
*
|
||||||
|
* @return array|null unserialized value wrapped in array or null
|
||||||
|
*/
|
||||||
|
private function includeCacheFile($file)
|
||||||
|
{
|
||||||
|
$valueArray = @include $file;
|
||||||
|
if (!is_array($valueArray) || 2 !== count($valueArray)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($serializedValue, $expiresAt) = $valueArray;
|
||||||
|
if (time() > (int) $expiresAt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unserializedValueInArray = unserialize($serializedValue);
|
||||||
|
if (!is_array($unserializedValueInArray)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $unserializedValueInArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $expiresAt
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function createCacheFileContent($value, $expiresAt)
|
||||||
|
{
|
||||||
|
$exportedValue = var_export(array(serialize([$value]), $expiresAt), true);
|
||||||
|
|
||||||
|
return '<?php return '.$exportedValue.';';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,260 @@
|
|||||||
|
<?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\Cache\Tests\Adapter;
|
||||||
|
|
||||||
|
use Cache\IntegrationTests\CachePoolTest;
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
|
||||||
|
abstract class AbstractAppendOnlyAdapterTest extends CachePoolTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var mixed
|
||||||
|
*/
|
||||||
|
private $cacheVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->cacheVersion = $this->createRandomCachePoolVersion();
|
||||||
|
$this->cache = $this->createVersionedCachePool($this->cacheVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCachePool()
|
||||||
|
{
|
||||||
|
$cacheVersion = $this->createRandomCachePoolVersion();
|
||||||
|
|
||||||
|
return $this->createVersionedCachePool($cacheVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed cache version that will be used by this adapter
|
||||||
|
*/
|
||||||
|
abstract public function createRandomCachePoolVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CacheItemPoolInterface that is used in the tests that need to recreate the same cache pool
|
||||||
|
*/
|
||||||
|
abstract public function createVersionedCachePool($cacheVersion);
|
||||||
|
|
||||||
|
public function testBasicUsage()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('4711');
|
||||||
|
$this->cache->save($item);
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key2');
|
||||||
|
$item->set('4712');
|
||||||
|
$this->cache->save($item);
|
||||||
|
|
||||||
|
$fooItem = $this->cache->getItem('key');
|
||||||
|
$this->assertTrue($fooItem->isHit());
|
||||||
|
$this->assertEquals('4711', $fooItem->get());
|
||||||
|
|
||||||
|
$barItem = $this->cache->getItem('key2');
|
||||||
|
$this->assertTrue($barItem->isHit());
|
||||||
|
$this->assertEquals('4712', $barItem->get());
|
||||||
|
|
||||||
|
// Removing must always return false
|
||||||
|
$this->assertFalse($this->cache->deleteItem('key'));
|
||||||
|
$this->assertTrue($this->cache->getItem('key')->isHit());
|
||||||
|
$this->assertTrue($this->cache->getItem('key2')->isHit());
|
||||||
|
|
||||||
|
// Remove everything
|
||||||
|
$this->assertFalse($this->cache->clear());
|
||||||
|
$this->assertTrue($this->cache->getItem('key')->isHit());
|
||||||
|
$this->assertTrue($this->cache->getItem('key2')->isHit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClear()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return = $this->cache->clear();
|
||||||
|
$this->assertTrue($return, 'clear() should return true when no items are in a cache');
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('value');
|
||||||
|
$this->cache->save($item);
|
||||||
|
|
||||||
|
$return = $this->cache->clear();
|
||||||
|
|
||||||
|
$this->assertFalse($return, 'clear() must return false for append-only cache when not empty.');
|
||||||
|
$this->assertTrue($this->cache->getItem('key')->isHit(), 'Item should still be in an append-only cache, even after clear.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteItem()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('value');
|
||||||
|
$this->cache->save($item);
|
||||||
|
|
||||||
|
$this->assertFalse($this->cache->deleteItem('key'));
|
||||||
|
$this->assertTrue($this->cache->getItem('key')->isHit(), 'A deleted item should still be a hit in an append-only cache.');
|
||||||
|
$this->assertTrue($this->cache->hasItem('key'), 'A deleted item should still be a hit in an append-only cache.');
|
||||||
|
|
||||||
|
$this->assertTrue($this->cache->deleteItem('key2'), 'Deleting an item that does not exist should return true.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteItems()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->cache->getItems(['foo', 'bar', 'baz']);
|
||||||
|
|
||||||
|
/** @var CacheItemInterface $item */
|
||||||
|
foreach ($items as $idx => $item) {
|
||||||
|
$item->set($idx);
|
||||||
|
$this->cache->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All should be a hit but 'biz'
|
||||||
|
$this->assertTrue($this->cache->getItem('foo')->isHit());
|
||||||
|
$this->assertTrue($this->cache->getItem('bar')->isHit());
|
||||||
|
$this->assertTrue($this->cache->getItem('baz')->isHit());
|
||||||
|
$this->assertFalse($this->cache->getItem('biz')->isHit());
|
||||||
|
|
||||||
|
$return = $this->cache->deleteItems(['foo', 'bar', 'biz']);
|
||||||
|
$this->assertFalse($return, 'Deleting should return false in append-only cache');
|
||||||
|
|
||||||
|
$this->assertTrue($this->cache->getItem('foo')->isHit(), 'Deleting shouldn\'t work for append-only cache');
|
||||||
|
$this->assertTrue($this->cache->getItem('bar')->isHit(), 'Deleting shouldn\'t work for append-only cache');
|
||||||
|
$this->assertTrue($this->cache->getItem('baz')->isHit(), 'Deleting shouldn\'t work for append-only cache');
|
||||||
|
$this->assertFalse($this->cache->getItem('biz')->isHit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveExpired()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('value');
|
||||||
|
$item->expiresAt(\DateTime::createFromFormat('U', time() - 1));
|
||||||
|
$this->cache->save($item);
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$this->assertFalse($item->isHit(), 'Cache should not return expired items');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveWithoutExpire()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('test_ttl_null');
|
||||||
|
$item->set('data');
|
||||||
|
$this->cache->save($item);
|
||||||
|
|
||||||
|
// Use a new pool instance to ensure that we don't it any caches
|
||||||
|
$pool = $this->createVersionedCachePool($this->cacheVersion);
|
||||||
|
$item = $pool->getItem('test_ttl_null');
|
||||||
|
|
||||||
|
$this->assertTrue($item->isHit(), 'Cache should have retrieved the items');
|
||||||
|
$this->assertEquals('data', $item->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteDeferredItem()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('4711');
|
||||||
|
$this->cache->saveDeferred($item);
|
||||||
|
|
||||||
|
$this->cache->deleteItem('key');
|
||||||
|
$this->assertFalse($this->cache->hasItem('key'), 'You must be able to delete a deferred item before committed. ');
|
||||||
|
$this->assertFalse($this->cache->getItem('key')->isHit(), 'You must be able to delete a deferred item before committed. ');
|
||||||
|
|
||||||
|
$this->cache->commit();
|
||||||
|
$this->assertFalse($this->cache->hasItem('key'), 'A deleted item should not reappear after commit. ');
|
||||||
|
$this->assertFalse($this->cache->getItem('key')->isHit(), 'A deleted item should not reappear after commit. ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeferredSaveWithoutCommit()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->prepareDeferredSaveWithoutCommit();
|
||||||
|
gc_collect_cycles();
|
||||||
|
|
||||||
|
$cache = $this->createVersionedCachePool($this->cacheVersion);
|
||||||
|
$this->assertTrue($cache->getItem('key')->isHit(), 'A deferred item should automatically be committed on CachePool::__destruct().');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveDeferredOverwrite()
|
||||||
|
{
|
||||||
|
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||||
|
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$item->set('value');
|
||||||
|
$this->cache->saveDeferred($item);
|
||||||
|
$item->set('new value');
|
||||||
|
$this->cache->saveDeferred($item);
|
||||||
|
|
||||||
|
$this->cache->commit();
|
||||||
|
$item = $this->cache->getItem('key');
|
||||||
|
$this->assertEquals('new value', $item->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepareDeferredSaveWithoutCommit()
|
||||||
|
{
|
||||||
|
$cache = $this->cache;
|
||||||
|
$this->cache = null;
|
||||||
|
|
||||||
|
$item = $cache->getItem('key');
|
||||||
|
$item->set('4711');
|
||||||
|
$cache->saveDeferred($item);
|
||||||
|
}
|
||||||
|
}
|
@ -19,15 +19,6 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
|||||||
*/
|
*/
|
||||||
class FilesystemAdapterTest extends CachePoolTest
|
class FilesystemAdapterTest extends CachePoolTest
|
||||||
{
|
{
|
||||||
public function createCachePool()
|
|
||||||
{
|
|
||||||
if (defined('HHVM_VERSION')) {
|
|
||||||
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FilesystemAdapter('sf-cache');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function tearDownAfterClass()
|
public static function tearDownAfterClass()
|
||||||
{
|
{
|
||||||
self::rmdir(sys_get_temp_dir().'/symfony-cache');
|
self::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
@ -54,4 +45,13 @@ class FilesystemAdapterTest extends CachePoolTest
|
|||||||
}
|
}
|
||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createCachePool()
|
||||||
|
{
|
||||||
|
if (defined('HHVM_VERSION')) {
|
||||||
|
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FilesystemAdapter('sf-cache');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
<?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\Cache\Tests\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group time-sensitive
|
||||||
|
*/
|
||||||
|
class PhpFilesAdapterTest extends AbstractAppendOnlyAdapterTest
|
||||||
|
{
|
||||||
|
public static function tearDownAfterClass()
|
||||||
|
{
|
||||||
|
self::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function rmdir($dir)
|
||||||
|
{
|
||||||
|
if (!file_exists($dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$dir || 0 !== strpos(dirname($dir), sys_get_temp_dir())) {
|
||||||
|
throw new \Exception(__METHOD__."() operates only on subdirs of system's temp dir");
|
||||||
|
}
|
||||||
|
$children = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||||
|
\RecursiveIteratorIterator::CHILD_FIRST
|
||||||
|
);
|
||||||
|
foreach ($children as $child) {
|
||||||
|
if ($child->isDir()) {
|
||||||
|
rmdir($child);
|
||||||
|
} else {
|
||||||
|
unlink($child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rmdir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cacheVersion
|
||||||
|
*
|
||||||
|
* @return CacheItemPoolInterface that is used in the tests that need to recreate the same cache pool
|
||||||
|
*/
|
||||||
|
public function createVersionedCachePool($cacheVersion)
|
||||||
|
{
|
||||||
|
if (defined('HHVM_VERSION')) {
|
||||||
|
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PhpFilesAdapter('sf-cache', 0, null, $cacheVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed cache version that will be used by this adapter
|
||||||
|
*/
|
||||||
|
public function createRandomCachePoolVersion()
|
||||||
|
{
|
||||||
|
return substr(str_replace('/', '-', base64_encode(md5(uniqid(mt_rand(), true), true))), 0, -2);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user