From 41d0e1df192f829c68270b54acac55867d82850b Mon Sep 17 00:00:00 2001 From: Nick Cinger Date: Mon, 27 Jul 2015 23:42:00 +0200 Subject: [PATCH] Cache add() method added for all Cache engines. --- lib/Cake/Cache/Cache.php | 37 +++++++++++++++++++ lib/Cake/Cache/CacheEngine.php | 12 +++++- lib/Cake/Cache/Engine/ApcEngine.php | 18 +++++++++ lib/Cake/Cache/Engine/FileEngine.php | 17 +++++++++ lib/Cake/Cache/Engine/MemcacheEngine.php | 20 ++++++++++ lib/Cake/Cache/Engine/MemcachedEngine.php | 20 ++++++++++ lib/Cake/Cache/Engine/RedisEngine.php | 19 ++++++++++ lib/Cake/Cache/Engine/WincacheEngine.php | 16 ++++++++ lib/Cake/Cache/Engine/XcacheEngine.php | 17 +++++++++ lib/Cake/Test/Case/Cache/CacheTest.php | 19 ++++++++++ .../Test/Case/Cache/Engine/ApcEngineTest.php | 19 ++++++++++ .../Test/Case/Cache/Engine/FileEngineTest.php | 18 +++++++++ .../Case/Cache/Engine/MemcacheEngineTest.php | 19 ++++++++++ .../Case/Cache/Engine/MemcachedEngineTest.php | 20 ++++++++++ .../Case/Cache/Engine/RedisEngineTest.php | 18 +++++++++ .../Case/Cache/Engine/WincacheEngineTest.php | 19 ++++++++++ .../Case/Cache/Engine/XcacheEngineTest.php | 20 ++++++++++ .../Lib/Cache/Engine/TestAppCacheEngine.php | 3 ++ .../Cache/Engine/TestPluginCacheEngine.php | 3 ++ 19 files changed, 333 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Cache/Cache.php b/lib/Cake/Cache/Cache.php index fb7e45de662..1f249b82b09 100644 --- a/lib/Cake/Cache/Cache.php +++ b/lib/Cake/Cache/Cache.php @@ -578,4 +578,41 @@ public static function remember($key, $callable, $config = 'default') { return $results; } +/** + * Write data for key into a cache engine if it doesn't exist already. + * + * ### Usage: + * + * Writing to the active cache config: + * + * `Cache::add('cached_data', $data);` + * + * Writing to a specific cache config: + * + * `Cache::add('cached_data', $data, 'long_term');` + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached - anything except a resource. + * @param string $config Optional string configuration name to write to. Defaults to 'default'. + * @return bool True if the data was successfully cached, false on failure. + */ + public static function add($key, $value, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return false; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + + if (!$key || is_resource($value)) { + return false; + } + + $success = self::$_engines[$config]->add($settings['prefix'] . $key, $value, $settings['duration']); + self::set(null, $config); + return $success; + } } diff --git a/lib/Cake/Cache/CacheEngine.php b/lib/Cake/Cache/CacheEngine.php index 6c611d2a904..6bad9f7ad96 100644 --- a/lib/Cake/Cache/CacheEngine.php +++ b/lib/Cake/Cache/CacheEngine.php @@ -83,6 +83,17 @@ public function gc($expires = null) { */ abstract public function write($key, $value, $duration); +/** + * Write value for a key into cache if it doesn't already exist + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param int $duration How long to cache for. + * @return bool True if the data was successfully cached, false on failure + */ + public function add($key, $value, $duration) { + } + /** * Read a key from the cache * @@ -176,5 +187,4 @@ public function key($key) { $key = preg_replace('/[\s]+/', '_', strtolower(trim(str_replace(array(DS, '/', '.'), '_', strval($key))))); return $prefix . $key; } - } diff --git a/lib/Cake/Cache/Engine/ApcEngine.php b/lib/Cake/Cache/Engine/ApcEngine.php index c2f0acfe1dc..da31651e31c 100644 --- a/lib/Cake/Cache/Engine/ApcEngine.php +++ b/lib/Cake/Cache/Engine/ApcEngine.php @@ -188,4 +188,22 @@ public function clearGroup($group) { return $success; } +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/function.apc-add.php + */ + public function add($key, $value, $duration) { + $expires = 0; + if ($duration) { + $expires = time() + $duration; + } + apc_add($key . '_expires', $expires, $duration); + return apc_add($key, $value, $duration); + } } diff --git a/lib/Cake/Cache/Engine/FileEngine.php b/lib/Cake/Cache/Engine/FileEngine.php index 43419155857..80f0f2d6ba4 100644 --- a/lib/Cake/Cache/Engine/FileEngine.php +++ b/lib/Cake/Cache/Engine/FileEngine.php @@ -429,4 +429,21 @@ public function clearGroup($group) { } return true; } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/lib/Cake/Cache/Engine/MemcacheEngine.php b/lib/Cake/Cache/Engine/MemcacheEngine.php index 250f02ad8ae..8deed88458b 100644 --- a/lib/Cake/Cache/Engine/MemcacheEngine.php +++ b/lib/Cake/Cache/Engine/MemcacheEngine.php @@ -289,4 +289,24 @@ public function groups() { public function clearGroup($group) { return (bool)$this->_Memcache->increment($this->settings['prefix'] . $group); } + +/** + * Write data for key into cache if it doesn't exist already. When using memcached as your cache engine + * remember that the Memcached pecl extension does not support cache expiry times greater + * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/memcache.add.php + */ + public function add($key, $value, $duration) { + if ($duration > 30 * DAY) { + $duration = 0; + } + + return $this->_Memcache->add($key, $value, $this->settings['compress'], $duration); + } } diff --git a/lib/Cake/Cache/Engine/MemcachedEngine.php b/lib/Cake/Cache/Engine/MemcachedEngine.php index a2611396a7d..28a1965428b 100644 --- a/lib/Cake/Cache/Engine/MemcachedEngine.php +++ b/lib/Cake/Cache/Engine/MemcachedEngine.php @@ -335,4 +335,24 @@ public function groups() { public function clearGroup($group) { return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group); } + +/** + * Write data for key into cache if it doesn't exist already. When using memcached as your cache engine + * remember that the Memcached pecl extension does not support cache expiry times greater + * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/memcached.add.php + */ + public function add($key, $value, $duration) { + if ($duration > 30 * DAY) { + $duration = 0; + } + + return $this->_Memcached->add($key, $value, $duration); + } } diff --git a/lib/Cake/Cache/Engine/RedisEngine.php b/lib/Cake/Cache/Engine/RedisEngine.php index f471efb51fa..d8c19aa6434 100644 --- a/lib/Cake/Cache/Engine/RedisEngine.php +++ b/lib/Cake/Cache/Engine/RedisEngine.php @@ -227,4 +227,23 @@ public function __destruct() { $this->_Redis->close(); } } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link https://github.com/phpredis/phpredis#setnx + */ + public function add($key, $value, $duration) { + $result = $this->_Redis->setnx($key, $value); + // setnx() doesn't have an expiry option, so overwrite the key with one + if ($result) { + return $this->_Redis->setex($key, $value, $duration); + } + return false; + } } diff --git a/lib/Cake/Cache/Engine/WincacheEngine.php b/lib/Cake/Cache/Engine/WincacheEngine.php index 39295571878..e5c9d7bd71a 100644 --- a/lib/Cake/Cache/Engine/WincacheEngine.php +++ b/lib/Cake/Cache/Engine/WincacheEngine.php @@ -188,4 +188,20 @@ public function clearGroup($group) { return $success; } +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/lib/Cake/Cache/Engine/XcacheEngine.php b/lib/Cake/Cache/Engine/XcacheEngine.php index 9c185560505..8075dde864f 100644 --- a/lib/Cake/Cache/Engine/XcacheEngine.php +++ b/lib/Cake/Cache/Engine/XcacheEngine.php @@ -207,4 +207,21 @@ protected function _auth($reverse = false) { } } } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/lib/Cake/Test/Case/Cache/CacheTest.php b/lib/Cake/Test/Case/Cache/CacheTest.php index 0bdb3f1fa2d..985adb4f6ea 100644 --- a/lib/Cake/Test/Case/Cache/CacheTest.php +++ b/lib/Cake/Test/Case/Cache/CacheTest.php @@ -519,4 +519,23 @@ public function testRemember() { public function cacher() { return 'This is some data ' . $this->_count; } + +/** + * Test add method. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'default'); + + $result = Cache::add('test_add_key', 'test data', 'default'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'default'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'default'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php index 91f019b6242..76b8382150e 100644 --- a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php @@ -273,4 +273,23 @@ public function testGroupClear() { $this->assertTrue(Cache::clearGroup('group_b', 'apc_groups')); $this->assertFalse(Cache::read('test_groups', 'apc_groups')); } + +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'apc'); + + $result = Cache::add('test_add_key', 'test data', 'apc'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'apc'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'apc'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php index cfd4ae91601..07cdac31078 100644 --- a/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/FileEngineTest.php @@ -550,4 +550,22 @@ public function testGroupClearNoPrefix() { $this->assertFalse(Cache::read('key_2', 'file_groups'), 'Did not delete'); } +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'file_test'); + + $result = Cache::add('test_add_key', 'test data', 'file_test'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'file_test'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'file_test'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index 36f8f0d8bd3..2060d1527e7 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -490,4 +490,23 @@ public function testGroupClear() { $this->assertTrue(Cache::clearGroup('group_b', 'memcache_groups')); $this->assertFalse(Cache::read('test_groups', 'memcache_groups')); } + +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'memcache'); + + $result = Cache::add('test_add_key', 'test data', 'memcache'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'memcache'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'memcache'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcachedEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcachedEngineTest.php index 17f1f99727d..072bf66e3d7 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcachedEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcachedEngineTest.php @@ -797,4 +797,24 @@ public function testGroupClear() { $this->assertTrue(Cache::clearGroup('group_b', 'memcached_groups')); $this->assertFalse(Cache::read('test_groups', 'memcached_groups')); } + +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::set(array('duration' => 1), null, 'memcached'); + Cache::delete('test_add_key', 'default'); + + $result = Cache::add('test_add_key', 'test data', 'default'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'default'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'default'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php index c3e19accadb..65000385ebf 100644 --- a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php @@ -381,4 +381,22 @@ public function testGroupClear() { $this->assertFalse(Cache::read('test_groups', 'redis_groups')); } +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'redis'); + + $result = Cache::add('test_add_key', 'test data', 'redis'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'redis'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'redis'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php index 2dd68c85cdc..316ee0040cc 100644 --- a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php @@ -259,4 +259,23 @@ public function testGroupClear() { $this->assertTrue(Cache::clearGroup('group_b', 'wincache_groups')); $this->assertFalse(Cache::read('test_groups', 'wincache_groups')); } + +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::delete('test_add_key', 'wincache'); + + $result = Cache::add('test_add_key', 'test data', 'wincache'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'wincache'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'wincache'); + $this->assertFalse($result); + } } diff --git a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php index fef083a1cb0..16c8643cb77 100644 --- a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php @@ -268,4 +268,24 @@ public function testGroupClear() { $this->assertTrue(Cache::clearGroup('group_b', 'xcache_groups')); $this->assertFalse(Cache::read('test_groups', 'xcache_groups')); } + +/** + * Test that failed add write return false. + * + * @return void + */ + public function testAdd() { + Cache::set(array('duration' => 1), null); + Cache::delete('test_add_key', 'default'); + + $result = Cache::add('test_add_key', 'test data', 'default'); + $this->assertTrue($result); + + $expected = 'test data'; + $result = Cache::read('test_add_key', 'default'); + $this->assertEquals($expected, $result); + + $result = Cache::add('test_add_key', 'test data 2', 'default'); + $this->assertFalse($result); + } } 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 263592db21e..4d89deb4326 100644 --- a/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php +++ b/lib/Cake/Test/test_app/Lib/Cache/Engine/TestAppCacheEngine.php @@ -46,4 +46,7 @@ public function clear($check) { public function clearGroup($group) { } + + public function add($key, $value, $duration) { + } } 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 aca459cbad2..6f2a4c7925f 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 @@ -43,4 +43,7 @@ public function clear($check) { public function clearGroup($group) { } + + public function add($key, $value, $duration) { + } }