diff --git a/lib/Cake/Cache/Cache.php b/lib/Cake/Cache/Cache.php index f69895d57..1a5aec1d9 100644 --- a/lib/Cake/Cache/Cache.php +++ b/lib/Cake/Cache/Cache.php @@ -91,6 +91,8 @@ class Cache { * The following keys are used in core cache engines: * * - `duration` Specify how long items in this cache configuration last. + * - `groups` List of groups or 'tags' associated to every key stored in this config. + * handy for deleting a complete group from cache. * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace * with either another cache config or another application. * - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable @@ -452,6 +454,22 @@ class Cache { return $success; } +/** + * Delete all keys from the cache belonging to the same group. + * + * @param string $group name of the group to be cleared + * @param string $config name of the configuration to use. Defaults to 'default' + * @return boolean True if the cache group was successfully cleared, false otherwise + */ + public static function clearGroup($group, $config = 'default') { + if (!self::isInitialized($config)) { + return false; + } + $success = self::$_engines[$config]->clearGroup($group); + self::set(null, $config); + return $success; + } + /** * Check if Cache has initialized a working config for the given name. * diff --git a/lib/Cake/Cache/CacheEngine.php b/lib/Cake/Cache/CacheEngine.php index f50621bd6..37fda942a 100644 --- a/lib/Cake/Cache/CacheEngine.php +++ b/lib/Cake/Cache/CacheEngine.php @@ -27,6 +27,14 @@ abstract class CacheEngine { */ public $settings = array(); +/** + * Contains the compiled string with all groups + * prefixes to be prepeded to every key in this cache engine + * + * @var string + **/ + protected $groupPrefix = null; + /** * Initialize the cache engine * @@ -37,10 +45,14 @@ abstract class CacheEngine { */ public function init($settings = array()) { $this->settings = array_merge( - array('prefix' => 'cake_', 'duration' => 3600, 'probability' => 100), + array('prefix' => 'cake_', 'duration' => 3600, 'probability' => 100, 'groups' => array()), $this->settings, $settings ); + if (!empty($this->settings['groups'])) { + sort($this->settings['groups']); + $this->groupPrefix = str_repeat('%s_', count($this->settings['groups'])); + } if (!is_numeric($this->settings['duration'])) { $this->settings['duration'] = strtotime($this->settings['duration']) - time(); } @@ -110,6 +122,29 @@ abstract class CacheEngine { */ abstract public function clear($check); +/** + * Clears all values belonging to a group. Is upt to the implementing engine + * to decide whether actually deete the keys or just simulate it to acheive + * the same result. + * + * @param string $groups name of the group to be cleared + * @return boolean + **/ + public function clearGroup($group) { + return false; + } + +/** + * Does whatever initialization for each group is required + * and returns the `group value` for each of them, this is + * the token representing each group in the cache key + * + * @return array + **/ + public function groups() { + return $this->settings['groups']; + } + /** * Cache Engine settings * @@ -129,8 +164,14 @@ abstract class CacheEngine { if (empty($key)) { return false; } + + $prefix = ''; + if (!empty($this->groupPrefix)) { + $prefix = vsprintf($this->groupPrefix, $this->groups()); + } + $key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key))); - return $key; + return $prefix . $key; } } diff --git a/lib/Cake/Cache/Engine/ApcEngine.php b/lib/Cake/Cache/Engine/ApcEngine.php index a453ecd1f..d9c155abb 100644 --- a/lib/Cake/Cache/Engine/ApcEngine.php +++ b/lib/Cake/Cache/Engine/ApcEngine.php @@ -25,6 +25,14 @@ */ class ApcEngine extends CacheEngine { +/** + * Contains the compiled group names + * (prefixed witht the global configuration prefix) + * + * @var array + **/ + protected $_compiledGroupNames = array(); + /** * Initialize the Cache Engine * @@ -127,4 +135,48 @@ class ApcEngine extends CacheEngine { return true; } +/** + * Returns the `group value` for each of the configured groups + * If the group initial value was not found, then it initializes + * the group accordingly. + * + * @return array + **/ + public function groups() { + if (empty($this->_compiledGroupNames)) { + foreach ($this->settings['groups'] as $group) { + $this->_compiledGroupNames[] = $this->settings['prefix'] . $group; + } + } + + $groups = apc_fetch($this->_compiledGroupNames); + if (count($groups) !== count($this->settings['groups'])) { + foreach ($this->_compiledGroupNames as $group) { + if (!isset($groups[$group])) { + apc_store($group, 1); + $groups[$group] = 1; + } + } + ksort($groups); + } + + $result = array(); + $groups = array_values($groups); + foreach ($this->settings['groups'] as $i => $group) { + $result[] = $group . $groups[$i]; + } + return $result; + } + +/** + * Increments the group value to simulate deletion of all keys under a group + * old values will remain in storage until they expire. + * + * @return boolean success + **/ + public function clearGroup($group) { + apc_inc($this->settings['prefix'] . $group, 1, $success); + return $success; + } + } diff --git a/lib/Cake/Cache/Engine/FileEngine.php b/lib/Cake/Cache/Engine/FileEngine.php index 7b011ac18..ecc2e2f79 100644 --- a/lib/Cake/Cache/Engine/FileEngine.php +++ b/lib/Cake/Cache/Engine/FileEngine.php @@ -82,6 +82,9 @@ class FileEngine extends CacheEngine { if (substr($this->settings['path'], -1) !== DS) { $this->settings['path'] .= DS; } + if (!empty($this->groupPrefix)) { + $this->groupPrefix = str_replace('_', DS, $this->groupPrefix); + } return $this->_active(); } @@ -285,7 +288,17 @@ class FileEngine extends CacheEngine { * @return boolean true if the cache key could be set, false otherwise */ protected function _setKey($key, $createKey = false) { - $path = new SplFileInfo($this->settings['path'] . $key); + + $groups = null; + if (!empty($this->groupPrefix)) { + $groups = vsprintf($this->groupPrefix, $this->groups()); + } + $dir = $this->settings['path'] . $groups; + + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } + $path = new SplFileInfo($dir . $key); if (!$createKey && !$path->isFile()) { return false; @@ -324,4 +337,35 @@ class FileEngine extends CacheEngine { return true; } +/** + * Generates a safe key for use with cache engine storage engines. + * + * @param string $key the key passed over + * @return mixed string $key or false + */ + public function key($key) { + if (empty($key)) { + return false; + } + + $key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key))); + return $key; + } + +/** + * Recursively deletes all files under any directory named as $group + * + * @return boolean success + **/ + public function clearGroup($group) { + $directoryIterator = new RecursiveDirectoryIterator($this->settings['path']); + $contents = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($contents as $object) { + $containsGroup = strpos($object->getPathName(), DS . $group . DS) !== false; + if ($object->isFile() && $containsGroup) { + unlink($object->getPathName()); + } + } + return true; + } } diff --git a/lib/Cake/Cache/Engine/MemcacheEngine.php b/lib/Cake/Cache/Engine/MemcacheEngine.php index e68035c3f..c50b83959 100644 --- a/lib/Cake/Cache/Engine/MemcacheEngine.php +++ b/lib/Cake/Cache/Engine/MemcacheEngine.php @@ -27,6 +27,14 @@ */ class MemcacheEngine extends CacheEngine { +/** + * Contains the compiled group names + * (prefixed witht the global configuration prefix) + * + * @var array + **/ + protected $_compiledGroupNames = array(); + /** * Memcache wrapper. * @@ -235,4 +243,47 @@ class MemcacheEngine extends CacheEngine { return true; } +/** + * Returns the `group value` for each of the configured groups + * If the group initial value was not found, then it initializes + * the group accordingly. + * + * @return array + **/ + public function groups() { + if (empty($this->_compiledGroupNames)) { + foreach ($this->settings['groups'] as $group) { + $this->_compiledGroupNames[] = $this->settings['prefix'] . $group; + } + } + + $groups = $this->_Memcache->get($this->_compiledGroupNames); + if (count($groups) !== count($this->settings['groups'])) { + foreach ($this->_compiledGroupNames as $group) { + if (!isset($groups[$group])) { + $this->_Memcache->set($group, 1, false, 0); + $groups[$group] = 1; + } + } + ksort($groups); + } + + $result = array(); + $groups = array_values($groups); + foreach ($this->settings['groups'] as $i => $group) { + $result[] = $group . $groups[$i]; + } + + return $result; + } + +/** + * Increments the group value to simulate deletion of all keys under a group + * old values will remain in storage until they expire. + * + * @return boolean success + **/ + public function clearGroup($group) { + return (bool) $this->_Memcache->increment($this->settings['prefix'] . $group); + } } diff --git a/lib/Cake/Cache/Engine/WincacheEngine.php b/lib/Cake/Cache/Engine/WincacheEngine.php index f6dc6de24..6a189dcc8 100644 --- a/lib/Cake/Cache/Engine/WincacheEngine.php +++ b/lib/Cake/Cache/Engine/WincacheEngine.php @@ -26,6 +26,14 @@ */ class WincacheEngine extends CacheEngine { +/** + * Contains the compiled group names + * (prefixed witht the global configuration prefix) + * + * @var array + **/ + protected $_compiledGroupNames = array(); + /** * Initialize the Cache Engine * @@ -134,4 +142,49 @@ class WincacheEngine extends CacheEngine { return true; } +/** + * Returns the `group value` for each of the configured groups + * If the group initial value was not found, then it initializes + * the group accordingly. + * + * @return array + **/ + public function groups() { + if (empty($this->_compiledGroupNames)) { + foreach ($this->settings['groups'] as $group) { + $this->_compiledGroupNames[] = $this->settings['prefix'] . $group; + } + } + + $groups = wincache_ucache_get($this->_compiledGroupNames); + if (count($groups) !== count($this->settings['groups'])) { + foreach ($this->_compiledGroupNames as $group) { + if (!isset($groups[$group])) { + wincache_ucache_set($group, 1); + $groups[$group] = 1; + } + } + ksort($groups); + } + + $result = array(); + $groups = array_values($groups); + foreach ($this->settings['groups'] as $i => $group) { + $result[] = $group . $groups[$i]; + } + return $result; + } + +/** + * Increments the group value to simulate deletion of all keys under a group + * old values will remain in storage until they expire. + * + * @return boolean success + **/ + public function clearGroup($group) { + wincache_ucache_inc($this->settings['prefix'] . $group, 1, $success); + return $success; + } + + } diff --git a/lib/Cake/Cache/Engine/XcacheEngine.php b/lib/Cake/Cache/Engine/XcacheEngine.php index d41cfdb6d..9bc6be219 100644 --- a/lib/Cake/Cache/Engine/XcacheEngine.php +++ b/lib/Cake/Cache/Engine/XcacheEngine.php @@ -137,6 +137,36 @@ class XcacheEngine extends CacheEngine { return true; } +/** + * Returns the `group value` for each of the configured groups + * If the group initial value was not found, then it initializes + * the group accordingly. + * + * @return array + **/ + public function groups() { + $result = array(); + foreach ($this->settings['groups'] as $group) { + $value = xcache_get($this->settings['prefix'] . $group); + if (!$value) { + $value = 1; + xcache_set($this->settings['prefix'] . $group, $value, 0); + } + $result[] = $group . $value; + } + return $result; + } + +/** + * Increments the group value to simulate deletion of all keys under a group + * old values will remain in storage until they expire. + * + * @return boolean success + **/ + public function clearGroup($group) { + return (bool) xcache_inc($this->settings['prefix'] . $group, 1); + } + /** * Populates and reverses $_SERVER authentication values * Makes necessary changes (and reverting them back) in $_SERVER @@ -173,5 +203,4 @@ class XcacheEngine extends CacheEngine { } } } - } diff --git a/lib/Cake/Test/Case/Cache/CacheTest.php b/lib/Cake/Test/Case/Cache/CacheTest.php index 93d146cfb..85e4ff62e 100644 --- a/lib/Cake/Test/Case/Cache/CacheTest.php +++ b/lib/Cake/Test/Case/Cache/CacheTest.php @@ -217,7 +217,8 @@ class CacheTest extends CakeTestCase { 'probability' => 100, 'engine' => 'File', 'isWindows' => DIRECTORY_SEPARATOR == '\\', - 'mask' => 0664 + 'mask' => 0664, + 'groups' => array() ); $this->assertEquals($expected, Cache::settings('sessions')); diff --git a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php index 9b1c9facb..aa73ca6f3 100644 --- a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php @@ -49,6 +49,7 @@ class ApcEngineTest extends CakeTestCase { parent::tearDown(); Configure::write('Cache.disable', $this->_cacheDisable); Cache::drop('apc'); + Cache::drop('apc_groups'); Cache::config('default'); } @@ -200,4 +201,73 @@ class ApcEngineTest extends CakeTestCase { $this->assertEquals('survive', apc_fetch('not_cake')); apc_delete('not_cake'); } + +/** + * Tests that configuring groups for stored keys return the correct values when read/written + * Shows that altering the group value is equivalent to deleting all keys under the same + * group + * + * @return void + */ + public function testGroupsReadWrite() { + Cache::config('apc_groups', array( + 'engine' => 'Apc', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'apc_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'apc_groups')); + + apc_inc('test_group_a'); + $this->assertFalse(Cache::read('test_groups', 'apc_groups')); + $this->assertTrue(Cache::write('test_groups', 'value2', 'apc_groups')); + $this->assertEquals('value2', Cache::read('test_groups', 'apc_groups')); + + apc_inc('test_group_b'); + $this->assertFalse(Cache::read('test_groups', 'apc_groups')); + $this->assertTrue(Cache::write('test_groups', 'value3', 'apc_groups')); + $this->assertEquals('value3', Cache::read('test_groups', 'apc_groups')); + } + +/** + * Tests that deleteing from a groups-enabled config is possible + * + * @return void + */ + public function testGroupDelete() { + Cache::config('apc_groups', array( + 'engine' => 'Apc', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'apc_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'apc_groups')); + $this->assertTrue(Cache::delete('test_groups', 'apc_groups')); + + $this->assertFalse(Cache::read('test_groups', 'apc_groups')); + } + +/** + * Test clearing a cache group + * + * @return void + **/ + public function testGroupClear() { + Cache::config('apc_groups', array( + 'engine' => 'Apc', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + + $this->assertTrue(Cache::write('test_groups', 'value', 'apc_groups')); + $this->assertTrue(Cache::clearGroup('group_a', 'apc_groups')); + $this->assertFalse(Cache::read('test_groups', 'apc_groups')); + + $this->assertTrue(Cache::write('test_groups', 'value2', 'apc_groups')); + $this->assertTrue(Cache::clearGroup('group_b', 'apc_groups')); + $this->assertFalse(Cache::read('test_groups', 'apc_groups')); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php index 6923d7f8a..4d4301be2 100644 --- a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php @@ -53,6 +53,9 @@ class FileEngineTest extends CakeTestCase { parent::tearDown(); Cache::clear(false, 'file_test'); Cache::drop('file_test'); + Cache::drop('file_groups'); + Cache::drop('file_groups2'); + Cache::drop('file_groups3'); } /** @@ -393,4 +396,60 @@ class FileEngineTest extends CakeTestCase { Cache::drop('mask_test'); } +/** + * Tests that configuring groups for stored keys return the correct values when read/written + * + * @return void + */ + public function testGroupsReadWrite() { + Cache::config('file_groups', array('engine' => 'File', 'duration' => 3600, 'groups' => array('group_a', 'group_b'))); + $this->assertTrue(Cache::write('test_groups', 'value', 'file_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'file_groups')); + + $this->assertTrue(Cache::write('test_groups2', 'value2', 'file_groups')); + $this->assertTrue(Cache::write('test_groups3', 'value3', 'file_groups')); + } + + /** + * Tests that deleteing from a groups-enabled config is possible + * + * @return void + */ + public function testGroupDelete() { + Cache::config('file_groups', array('engine' => 'File', 'duration' => 3600, 'groups' => array('group_a', 'group_b'))); + $this->assertTrue(Cache::write('test_groups', 'value', 'file_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'file_groups')); + $this->assertTrue(Cache::delete('test_groups', 'file_groups')); + + $this->assertFalse(Cache::read('test_groups', 'file_groups')); + } + +/** + * Test clearing a cache group + * + * @return void + **/ + public function testGroupClear() { + Cache::config('file_groups', array('engine' => 'File', 'duration' => 3600, 'groups' => array('group_a', 'group_b'))); + Cache::config('file_groups2', array('engine' => 'File', 'duration' => 3600, 'groups' => array('group_b'))); + Cache::config('file_groups3', array('engine' => 'File', 'duration' => 3600, 'groups' => array('group_a'))); + + $this->assertTrue(Cache::write('test_groups', 'value', 'file_groups')); + $this->assertTrue(Cache::write('test_groups2', 'value', 'file_groups2')); + $this->assertTrue(Cache::write('test_groups3', 'value', 'file_groups3')); + + $this->assertTrue(Cache::clearGroup('group_a', 'file_groups')); + $this->assertFalse(Cache::read('test_groups', 'file_groups')); + $this->assertEquals('value', Cache::read('test_groups2', 'file_groups2')); + $this->assertFalse(Cache::read('test_groups3', 'file_groups3')); + + $this->assertTrue(Cache::write('test_groups4', 'value', 'file_groups')); + $this->assertTrue(Cache::write('test_groups5', 'value', 'file_groups2')); + $this->assertTrue(Cache::write('test_groups6', 'value', 'file_groups3')); + + $this->assertTrue(Cache::clearGroup('group_b', 'file_groups')); + $this->assertFalse(Cache::read('test_groups4', 'file_groups')); + $this->assertFalse(Cache::read('test_groups5', 'file_groups2')); + $this->assertEquals('value', Cache::read('test_groups6', 'file_groups3')); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index 6ff16437b..47a7f0d21 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -72,6 +72,8 @@ class MemcacheEngineTest extends CakeTestCase { parent::tearDown(); Configure::write('Cache.disable', $this->_cacheDisable); Cache::drop('memcache'); + Cache::drop('memcache_groups'); + Cache::drop('memcache_helper'); Cache::config('default'); } @@ -92,6 +94,7 @@ class MemcacheEngineTest extends CakeTestCase { 'compress' => false, 'engine' => 'Memcache', 'persistent' => true, + 'groups' => array() ); $this->assertEquals($expecting, $settings); } @@ -378,7 +381,7 @@ class MemcacheEngineTest extends CakeTestCase { Cache::config('memcache', array('duration' => 0)); $result = Cache::write('test_key', 'written!', 'memcache'); - $this->assertTrue('Could not write with duration 0', $result); + $this->assertTrue($result); $result = Cache::read('test_key', 'memcache'); $this->assertEquals('written!', $result); } @@ -402,4 +405,75 @@ class MemcacheEngineTest extends CakeTestCase { $memcache->write('key', $value, 50 * DAY); } +/** + * Tests that configuring groups for stored keys return the correct values when read/written + * Shows that altering the group value is equivalent to deleting all keys under the same + * group + * + * @return void + */ + public function testGroupReadWrite() { + Cache::config('memcache_groups', array( + 'engine' => 'Memcache', + 'duration' => 3600, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + Cache::config('memcache_helper', array( + 'engine' => 'Memcache', + 'duration' => 3600, + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'memcache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'memcache_groups')); + + Cache::increment('group_a', 1, 'memcache_helper'); + $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value2', 'memcache_groups')); + $this->assertEquals('value2', Cache::read('test_groups', 'memcache_groups')); + + Cache::increment('group_b', 1, 'memcache_helper'); + $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value3', 'memcache_groups')); + $this->assertEquals('value3', Cache::read('test_groups', 'memcache_groups')); + } + +/** + * Tests that deleteing from a groups-enabled config is possible + * + * @return void + */ + public function testGroupDelete() { + Cache::config('memcache_groups', array( + 'engine' => 'Memcache', + 'duration' => 3600, + 'groups' => array('group_a', 'group_b') + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'memcache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'memcache_groups')); + $this->assertTrue(Cache::delete('test_groups', 'memcache_groups')); + + $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); + } + +/** + * Test clearing a cache group + * + * @return void + **/ + public function testGroupClear() { + Cache::config('memcache_groups', array( + 'engine' => 'Memcache', + 'duration' => 3600, + 'groups' => array('group_a', 'group_b') + )); + + $this->assertTrue(Cache::write('test_groups', 'value', 'memcache_groups')); + $this->assertTrue(Cache::clearGroup('group_a', 'memcache_groups')); + $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); + + $this->assertTrue(Cache::write('test_groups', 'value2', 'memcache_groups')); + $this->assertTrue(Cache::clearGroup('group_b', 'memcache_groups')); + $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php index b952868e7..ace401147 100644 --- a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php @@ -48,6 +48,7 @@ class WincacheEngineTest extends CakeTestCase { parent::tearDown(); Configure::write('Cache.disable', $this->_cacheDisable); Cache::drop('wincache'); + Cache::drop('wincache_groups'); Cache::config('default'); } @@ -190,4 +191,73 @@ class WincacheEngineTest extends CakeTestCase { $this->assertFalse(Cache::read('some_value', 'wincache')); $this->assertEquals('safe', wincache_ucache_get('not_cake')); } + +/** + * Tests that configuring groups for stored keys return the correct values when read/written + * Shows that altering the group value is equivalent to deleting all keys under the same + * group + * + * @return void + */ + public function testGroupsReadWrite() { + Cache::config('wincache_groups', array( + 'engine' => 'Wincache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'wincache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'wincache_groups')); + + wincache_ucache_inc('test_group_a'); + $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value2', 'wincache_groups')); + $this->assertEquals('value2', Cache::read('test_groups', 'wincache_groups')); + + wincache_ucache_inc('test_group_b'); + $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value3', 'wincache_groups')); + $this->assertEquals('value3', Cache::read('test_groups', 'wincache_groups')); + } + +/** + * Tests that deleteing from a groups-enabled config is possible + * + * @return void + */ + public function testGroupDelete() { + Cache::config('wincache_groups', array( + 'engine' => 'Wincache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'wincache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'wincache_groups')); + $this->assertTrue(Cache::delete('test_groups', 'wincache_groups')); + + $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); + } + +/** + * Test clearing a cache group + * + * @return void + **/ + public function testGroupClear() { + Cache::config('wincache_groups', array( + 'engine' => 'Wincache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + + $this->assertTrue(Cache::write('test_groups', 'value', 'wincache_groups')); + $this->assertTrue(Cache::clearGroup('group_a', 'wincache_groups')); + $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); + + $this->assertTrue(Cache::write('test_groups', 'value2', 'wincache_groups')); + $this->assertTrue(Cache::clearGroup('group_b', 'wincache_groups')); + $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php index e6d4ff572..3f4911a8e 100644 --- a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php @@ -36,6 +36,7 @@ class XcacheEngineTest extends CakeTestCase { if (!function_exists('xcache_set')) { $this->markTestSkipped('Xcache is not installed or configured properly'); } + $this->_cacheDisable = Configure::read('Cache.disable'); Configure::write('Cache.disable', false); Cache::config('xcache', array('engine' => 'Xcache', 'prefix' => 'cake_')); } @@ -47,7 +48,10 @@ class XcacheEngineTest extends CakeTestCase { */ public function tearDown() { parent::tearDown(); + Configure::write('Cache.disable', $this->_cacheDisable); Cache::drop('xcache'); + Cache::drop('xcache_groups'); + Cache::config('default'); } /** @@ -196,4 +200,73 @@ class XcacheEngineTest extends CakeTestCase { $result = Cache::read('test_increment'); $this->assertEquals(8, $result); } + +/** + * Tests that configuring groups for stored keys return the correct values when read/written + * Shows that altering the group value is equivalent to deleting all keys under the same + * group + * + * @return void + */ + public function testGroupsReadWrite() { + Cache::config('xcache_groups', array( + 'engine' => 'Xcache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'xcache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'xcache_groups')); + + xcache_inc('test_group_a', 1); + $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value2', 'xcache_groups')); + $this->assertEquals('value2', Cache::read('test_groups', 'xcache_groups')); + + xcache_inc('test_group_b', 1); + $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); + $this->assertTrue(Cache::write('test_groups', 'value3', 'xcache_groups')); + $this->assertEquals('value3', Cache::read('test_groups', 'xcache_groups')); + } + +/** + * Tests that deleteing from a groups-enabled config is possible + * + * @return void + */ + public function testGroupDelete() { + Cache::config('xcache_groups', array( + 'engine' => 'Xcache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + $this->assertTrue(Cache::write('test_groups', 'value', 'xcache_groups')); + $this->assertEquals('value', Cache::read('test_groups', 'xcache_groups')); + $this->assertTrue(Cache::delete('test_groups', 'xcache_groups')); + + $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); + } + +/** + * Test clearing a cache group + * + * @return void + **/ + public function testGroupClear() { + Cache::config('xcache_groups', array( + 'engine' => 'Xcache', + 'duration' => 0, + 'groups' => array('group_a', 'group_b'), + 'prefix' => 'test_' + )); + + $this->assertTrue(Cache::write('test_groups', 'value', 'xcache_groups')); + $this->assertTrue(Cache::clearGroup('group_a', 'xcache_groups')); + $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); + + $this->assertTrue(Cache::write('test_groups', 'value2', 'xcache_groups')); + $this->assertTrue(Cache::clearGroup('group_b', 'xcache_groups')); + $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); + } } diff --git a/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php b/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php index 1cdf3a46f..36d86b8cb 100644 --- a/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php +++ b/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php @@ -38,4 +38,7 @@ class TestAppCacheEngine extends CacheEngine { public function clear($check) { } + + public function clearGroup($group) { + } } diff --git a/lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Cache/Engine/TestPluginCacheEngine.php b/lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Cache/Engine/TestPluginCacheEngine.php index f8fb39214..3b91fcbb1 100644 --- a/lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Cache/Engine/TestPluginCacheEngine.php +++ b/lib/Cake/Test/test_app/Plugin/TestPlugin/Lib/Cache/Engine/TestPluginCacheEngine.php @@ -36,4 +36,6 @@ class TestPluginCacheEngine extends CacheEngine { public function clear($check) { } + public function clearGroup($group) { + } } diff --git a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp index 07b55281e..0933f2f26 100644 --- a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp +++ b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp @@ -43,6 +43,9 @@ echo 'Settings: