From 5d3c7eb16fc229cab3b3c25b8da2786b5f37080c Mon Sep 17 00:00:00 2001 From: Daniel Karp Date: Mon, 21 Oct 2013 20:39:10 -0400 Subject: [PATCH 1/9] Renaming Section.php file Renaming in a separate commit to retain git history --- src/Illuminate/Cache/{Section.php => TaggedCache.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Illuminate/Cache/{Section.php => TaggedCache.php} (100%) mode change 100755 => 100644 diff --git a/src/Illuminate/Cache/Section.php b/src/Illuminate/Cache/TaggedCache.php old mode 100755 new mode 100644 similarity index 100% rename from src/Illuminate/Cache/Section.php rename to src/Illuminate/Cache/TaggedCache.php From 7e6c2a3d19015c22269efbcfed4876ae71592360 Mon Sep 17 00:00:00 2001 From: Daniel Karp Date: Mon, 21 Oct 2013 20:42:55 -0400 Subject: [PATCH 2/9] Cache tags feature Changes cache Sections to Tags, and allows multiple tags per cache. --- src/Illuminate/Cache/ApcStore.php | 13 +-- src/Illuminate/Cache/ArrayStore.php | 13 +-- src/Illuminate/Cache/MemcachedStore.php | 13 +-- src/Illuminate/Cache/RedisSection.php | 28 ++++++- src/Illuminate/Cache/RedisStore.php | 8 ++ src/Illuminate/Cache/TaggableStore.php | 37 +++++++++ src/Illuminate/Cache/TaggedCache.php | 76 ++++------------- src/Illuminate/Cache/Tags.php | 106 ++++++++++++++++++++++++ src/Illuminate/Cache/WinCacheStore.php | 13 +-- src/Illuminate/Cache/XCacheStore.php | 13 +-- tests/Cache/CacheTaggedCacheTest.php | 38 +++++++++ 11 files changed, 236 insertions(+), 122 deletions(-) create mode 100644 src/Illuminate/Cache/TaggableStore.php create mode 100644 src/Illuminate/Cache/Tags.php create mode 100644 tests/Cache/CacheTaggedCacheTest.php diff --git a/src/Illuminate/Cache/ApcStore.php b/src/Illuminate/Cache/ApcStore.php index c7fa53c669a5..d81282d4456f 100755 --- a/src/Illuminate/Cache/ApcStore.php +++ b/src/Illuminate/Cache/ApcStore.php @@ -1,6 +1,6 @@ apc->flush(); } - /** - * Begin executing a new section operation. - * - * @param string $name - * @return \Illuminate\Cache\Section - */ - public function section($name) - { - return new Section($this, $name); - } - /** * Get the cache key prefix. * diff --git a/src/Illuminate/Cache/ArrayStore.php b/src/Illuminate/Cache/ArrayStore.php index eb6e927e5efb..ddabf3c8e446 100755 --- a/src/Illuminate/Cache/ArrayStore.php +++ b/src/Illuminate/Cache/ArrayStore.php @@ -1,6 +1,6 @@ storage = array(); } - /** - * Begin executing a new section operation. - * - * @param string $name - * @return \Illuminate\Cache\Section - */ - public function section($name) - { - return new Section($this, $name); - } - /** * Get the cache key prefix. * diff --git a/src/Illuminate/Cache/MemcachedStore.php b/src/Illuminate/Cache/MemcachedStore.php index a073e92df3cc..8ba5dfe1d304 100755 --- a/src/Illuminate/Cache/MemcachedStore.php +++ b/src/Illuminate/Cache/MemcachedStore.php @@ -2,7 +2,7 @@ use Memcached; -class MemcachedStore implements StoreInterface { +class MemcachedStore extends TaggableStore implements StoreInterface { /** * The Memcached instance. @@ -117,17 +117,6 @@ public function flush() $this->memcached->flush(); } - /** - * Begin executing a new section operation. - * - * @param string $name - * @return \Illuminate\Cache\Section - */ - public function section($name) - { - return new Section($this, $name); - } - /** * Get the underlying Memcached connection. * diff --git a/src/Illuminate/Cache/RedisSection.php b/src/Illuminate/Cache/RedisSection.php index bcd4266fb676..fecba67f11c3 100755 --- a/src/Illuminate/Cache/RedisSection.php +++ b/src/Illuminate/Cache/RedisSection.php @@ -1,6 +1,26 @@ name = $name; + } /** * Store an item in the cache indefinitely. @@ -13,7 +33,7 @@ public function forever($key, $value) { $this->store->connection()->lpush($this->foreverKey(), $key); - $this->store->forever($this->sectionItemKey($key), $value); + parent::forever($key, $value); } /** @@ -56,7 +76,7 @@ protected function getForeverKeys() return array_map(function($x) use ($me) { - return $me->getPrefix().$me->sectionItemKey($x); + return $me->getPrefix().$me->taggedItemKey($x); }, array_unique($this->store->connection()->lrange($this->foreverKey(), 0, -1))); } @@ -68,7 +88,7 @@ protected function getForeverKeys() */ protected function foreverKey() { - return $this->sectionKey().':forever'; + return $this->tags->tagKey($this->name).':forever'; } /** diff --git a/src/Illuminate/Cache/RedisStore.php b/src/Illuminate/Cache/RedisStore.php index 101c267ff91c..7fc185e5c569 100755 --- a/src/Illuminate/Cache/RedisStore.php +++ b/src/Illuminate/Cache/RedisStore.php @@ -141,6 +141,14 @@ public function section($name) return new RedisSection($this, $name); } + /** + * alias for section + */ + public function tag($name) + { + return $this->section($name); + } + /** * Get the Redis connection instance. * diff --git a/src/Illuminate/Cache/TaggableStore.php b/src/Illuminate/Cache/TaggableStore.php new file mode 100644 index 000000000000..8dcced4a4fcf --- /dev/null +++ b/src/Illuminate/Cache/TaggableStore.php @@ -0,0 +1,37 @@ +tag($name); + } + + /** + * Begin executing a new tags operation. + * + * @param string $name + * @return \Illuminate\Cache\TaggedCache + */ + public function tags($names) + { + return new TaggedCache($this, $names); + } + + /** + * Begin executing a new tags operation. + * + * @param string $name + * @return \Illuminate\Cache\TaggedCache + */ + public function tag($name) + { + return $this->tags(array($name)); + } +} diff --git a/src/Illuminate/Cache/TaggedCache.php b/src/Illuminate/Cache/TaggedCache.php index 36d4c443ceb6..181ef7835679 100644 --- a/src/Illuminate/Cache/TaggedCache.php +++ b/src/Illuminate/Cache/TaggedCache.php @@ -2,7 +2,7 @@ use Closure; -class Section { +class TaggedCache { /** * The cache store implementation. @@ -12,22 +12,22 @@ class Section { protected $store; /** - * The section name. + * The tag names. * - * @var string + * @var array */ - protected $name; + protected $tags; /** - * Create a new section instance. + * Create a new tagged cache instance. * * @param \Illuminate\Cache\StoreInterface $store - * @param string $name + * @param string $names * @return void */ - public function __construct(StoreInterface $store, $name) + public function __construct(StoreInterface $store, $names) { - $this->name = $name; + $this->tags = new Tags($store, $names); $this->store = $store; } @@ -51,7 +51,7 @@ public function has($key) */ public function get($key, $default = null) { - $value = $this->store->get($this->sectionItemKey($key)); + $value = $this->store->get($this->taggedItemKey($key)); return ! is_null($value) ? $value : value($default); } @@ -66,7 +66,7 @@ public function get($key, $default = null) */ public function put($key, $value, $minutes) { - return $this->store->put($this->sectionItemKey($key), $value, $minutes); + return $this->store->put($this->taggedItemKey($key), $value, $minutes); } /** @@ -78,7 +78,7 @@ public function put($key, $value, $minutes) */ public function increment($key, $value = 1) { - $this->store->increment($this->sectionItemKey($key), $value); + $this->store->increment($this->taggedItemKey($key), $value); } /** @@ -90,7 +90,7 @@ public function increment($key, $value = 1) */ public function decrement($key, $value = 1) { - $this->store->decrement($this->sectionItemKey($key), $value); + $this->store->decrement($this->taggedItemKey($key), $value); } /** @@ -102,7 +102,7 @@ public function decrement($key, $value = 1) */ public function forever($key, $value) { - $this->store->forever($this->sectionItemKey($key), $value); + $this->store->forever($this->taggedItemKey($key), $value); } /** @@ -113,7 +113,7 @@ public function forever($key, $value) */ public function forget($key) { - $this->store->forget($this->sectionItemKey($key)); + $this->store->forget($this->taggedItemKey($key)); } /** @@ -123,7 +123,7 @@ public function forget($key) */ public function flush() { - $this->reset(); + $this->tags->reset(); } /** @@ -178,53 +178,13 @@ public function rememberForever($key, Closure $callback) } /** - * Get a fully qualified section item key. + * Get a fully qualified key for a tagged item. * * @param string $key * @return string */ - public function sectionItemKey($key) + public function taggedItemKey($key) { - return $this->name.':'.$this->sectionId().':'.$key; + return $this->tags->getNamespace().':'.$key; } - - /** - * Reset the section, returning a new section identifier - * - * @return string - */ - protected function reset() - { - $this->store->forever($this->sectionKey(), $id = uniqid()); - - return $id; - } - - /** - * Get the unique section identifier. - * - * @return string - */ - protected function sectionId() - { - $id = $this->store->get($this->sectionKey()); - - if (is_null($id)) - { - $id = $this->reset(); - } - - return $id; - } - - /** - * Get the section identifier key. - * - * @return string - */ - protected function sectionKey() - { - return 'section:'.$this->name.':key'; - } - } diff --git a/src/Illuminate/Cache/Tags.php b/src/Illuminate/Cache/Tags.php new file mode 100644 index 000000000000..85d563ef245e --- /dev/null +++ b/src/Illuminate/Cache/Tags.php @@ -0,0 +1,106 @@ +names = $names; + $this->store = $store; + } + + /** + * Reset all tags + * + * @return string + */ + public function reset() + { + foreach($this->names as $name) + $this->resetTag($name); + } + + /** + * Get the unique tag identifier. + * + * @param string the name for a given tag + * @return string + */ + protected function tagId($name) + { + $id = $this->store->get($this->tagKey($name)); + + if (is_null($id)) + { + $id = $this->resetTag($name); + } + + return $id; + } + + /** + * get an array of tag identifiers for all tags + * @return array + */ + protected function tagIds() + { + $ids = array(); + foreach ($this->names as $name) + $ids[] = $this->tagId($name); + return $ids; + } + + /** + * get a unique namespace that will change if any of the tags are flushed + * @return string + */ + public function getNamespace() + { + //The sha1 ensures that the namespace is not too long, but is otherwise unnecessary + return sha1(implode('|', $this->tagIds())); + } + + /** + * Reset the tag, returning a new tag identifier + * + * @param string $name + * @return string + */ + protected function resetTag($name) + { + $this->store->forever($this->tagKey($name), $id = uniqid()); + + return $id; + } + + /** + * Get the tag identifier key for a given tag. + * + * @param string $name + * @return string + */ + public function tagKey($name) + { + return 'tag:'.$name.':key'; + } +} \ No newline at end of file diff --git a/src/Illuminate/Cache/WinCacheStore.php b/src/Illuminate/Cache/WinCacheStore.php index 66c56c4893b4..29307aa281b2 100755 --- a/src/Illuminate/Cache/WinCacheStore.php +++ b/src/Illuminate/Cache/WinCacheStore.php @@ -1,6 +1,6 @@ section('bop')->put('foo', 'bar', 10); + $store->section('zap')->put('baz', 'boom', 10); + $store->section('bop')->flush(); + $this->assertNull($store->section('bop')->get('foo')); + $this->assertEquals('boom', $store->section('zap')->get('baz')); + } + + + public function testCacheCanBeSavedWithMultipleTags() + { + $store = new ArrayStore; + $tags = array('bop', 'zap'); + $store->tags($tags)->put('foo', 'bar', 10); + $this->assertEquals('bar', $store->tags($tags)->get('foo')); + } + + + public function testCacheSavedWithMultipleTagsCanBeFlushed() + { + $store = new ArrayStore; + $tags1 = array('bop', 'zap'); + $store->tags($tags1)->put('foo', 'bar', 10); + $tags2 = array('bam', 'pow'); + $store->tags($tags2)->put('foo', 'bar', 10); + $store->tag('zap')->flush(); + $this->assertNull($store->tags($tags1)->get('foo')); + $this->assertEquals('bar', $store->tags($tags2)->get('foo')); + } +} \ No newline at end of file From 550294a39104859dbb1b4667b69e1d2ebc53cd33 Mon Sep 17 00:00:00 2001 From: Daniel Karp Date: Tue, 22 Oct 2013 22:30:06 -0400 Subject: [PATCH 3/9] Make TaggedCache implement StoreInterface This way, a tagged cache can be passed to a class that expects a StoreInterface, and doesn't have to know that it is dealing with a tagged cache. Tag-related operations can be handled outside the class. --- src/Illuminate/Cache/TaggedCache.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Cache/TaggedCache.php b/src/Illuminate/Cache/TaggedCache.php index 181ef7835679..7c16cf69fc81 100644 --- a/src/Illuminate/Cache/TaggedCache.php +++ b/src/Illuminate/Cache/TaggedCache.php @@ -2,7 +2,7 @@ use Closure; -class TaggedCache { +class TaggedCache implements StoreInterface { /** * The cache store implementation. @@ -187,4 +187,14 @@ public function taggedItemKey($key) { return $this->tags->getNamespace().':'.$key; } + + /** + * Get the cache key prefix. + * + * @return string + */ + public function getPrefix() + { + return $this->store->getPrefix(); + } } From 6bbfa3bf5a698e75bc2f417a44fe63eefd038ba6 Mon Sep 17 00:00:00 2001 From: Daniel Karp Date: Mon, 28 Oct 2013 12:22:16 -0400 Subject: [PATCH 4/9] Change the name of the Tags class to TagSet --- src/Illuminate/Cache/{Tags.php => TagSet.php} | 4 ++-- src/Illuminate/Cache/TaggedCache.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Illuminate/Cache/{Tags.php => TagSet.php} (97%) diff --git a/src/Illuminate/Cache/Tags.php b/src/Illuminate/Cache/TagSet.php similarity index 97% rename from src/Illuminate/Cache/Tags.php rename to src/Illuminate/Cache/TagSet.php index 85d563ef245e..66a596d66dbc 100644 --- a/src/Illuminate/Cache/Tags.php +++ b/src/Illuminate/Cache/TagSet.php @@ -1,6 +1,6 @@ tags = new Tags($store, $names); + $this->tags = new TagSet($store, $names); $this->store = $store; } From e18c11fb55b7bd36396e2054166515c2b6d1567b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Nov 2013 11:16:43 -0500 Subject: [PATCH 5/9] Working on cache tags. --- src/Illuminate/Cache/RedisSection.php | 3 ++- src/Illuminate/Cache/TagSet.php | 11 ++++++----- src/Illuminate/Cache/TaggableStore.php | 11 ++++++----- src/Illuminate/Cache/TaggedCache.php | 3 ++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Illuminate/Cache/RedisSection.php b/src/Illuminate/Cache/RedisSection.php index fecba67f11c3..2d2a7e1c5b4b 100755 --- a/src/Illuminate/Cache/RedisSection.php +++ b/src/Illuminate/Cache/RedisSection.php @@ -18,7 +18,8 @@ class RedisSection extends TaggedCache { */ public function __construct(StoreInterface $store, $name) { - parent::$construct($store, array($name)); + parent::__construct($store, array($name)); + $this->name = $name; } diff --git a/src/Illuminate/Cache/TagSet.php b/src/Illuminate/Cache/TagSet.php index 66a596d66dbc..995c209f0955 100644 --- a/src/Illuminate/Cache/TagSet.php +++ b/src/Illuminate/Cache/TagSet.php @@ -60,24 +60,24 @@ protected function tagId($name) /** * get an array of tag identifiers for all tags - * @return array + * @return array */ protected function tagIds() { $ids = array(); - foreach ($this->names as $name) + foreach ($this->names as $name) $ids[] = $this->tagId($name); return $ids; } /** * get a unique namespace that will change if any of the tags are flushed - * @return string + * @return string */ public function getNamespace() { //The sha1 ensures that the namespace is not too long, but is otherwise unnecessary - return sha1(implode('|', $this->tagIds())); + return sha1(implode('|', $this->tagIds())); } /** @@ -90,7 +90,7 @@ protected function resetTag($name) { $this->store->forever($this->tagKey($name), $id = uniqid()); - return $id; + return $id; } /** @@ -103,4 +103,5 @@ public function tagKey($name) { return 'tag:'.$name.':key'; } + } \ No newline at end of file diff --git a/src/Illuminate/Cache/TaggableStore.php b/src/Illuminate/Cache/TaggableStore.php index 8dcced4a4fcf..e27d3bfc6c31 100644 --- a/src/Illuminate/Cache/TaggableStore.php +++ b/src/Illuminate/Cache/TaggableStore.php @@ -19,19 +19,20 @@ public function section($name) * @param string $name * @return \Illuminate\Cache\TaggedCache */ - public function tags($names) + public function tag($name) { - return new TaggedCache($this, $names); + return $this->tags(array($name)); } /** * Begin executing a new tags operation. * - * @param string $name + * @param array|dynamic $names * @return \Illuminate\Cache\TaggedCache */ - public function tag($name) + public function tags($names) { - return $this->tags(array($name)); + return new TaggedCache($this, is_array($names) ? $names : func_get_args()); } + } diff --git a/src/Illuminate/Cache/TaggedCache.php b/src/Illuminate/Cache/TaggedCache.php index cd6a64c5409c..890958564df8 100644 --- a/src/Illuminate/Cache/TaggedCache.php +++ b/src/Illuminate/Cache/TaggedCache.php @@ -27,8 +27,8 @@ class TaggedCache implements StoreInterface { */ public function __construct(StoreInterface $store, $names) { - $this->tags = new TagSet($store, $names); $this->store = $store; + $this->tags = new TagSet($store, $names); } /** @@ -197,4 +197,5 @@ public function getPrefix() { return $this->store->getPrefix(); } + } From 68a68892c810f70e3feeb90ba386fdaa3c80d652 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Nov 2013 11:37:51 -0500 Subject: [PATCH 6/9] Work on redis tag caching. --- src/Illuminate/Cache/RedisStore.php | 20 +++----- src/Illuminate/Cache/RedisTaggedCache.php | 31 ++++++++++++ src/Illuminate/Cache/TagSet.php | 61 ++++++++++++----------- 3 files changed, 69 insertions(+), 43 deletions(-) create mode 100644 src/Illuminate/Cache/RedisTaggedCache.php diff --git a/src/Illuminate/Cache/RedisStore.php b/src/Illuminate/Cache/RedisStore.php index 7fc185e5c569..7ad4d6cd590f 100755 --- a/src/Illuminate/Cache/RedisStore.php +++ b/src/Illuminate/Cache/RedisStore.php @@ -2,7 +2,7 @@ use Illuminate\Redis\Database as Redis; -class RedisStore implements StoreInterface { +class RedisStore extends TaggableStore implements StoreInterface { /** * The Redis database connection. @@ -131,22 +131,14 @@ public function flush() } /** - * Begin executing a new section operation. + * Begin executing a new tags operation. * - * @param string $name - * @return \Illuminate\Cache\RedisSection + * @param array|dynamic $names + * @return \Illuminate\Cache\RedisTaggedCache */ - public function section($name) + public function tags($names) { - return new RedisSection($this, $name); - } - - /** - * alias for section - */ - public function tag($name) - { - return $this->section($name); + return new RedisTaggedCache($this, is_array($names) ? $names : func_get_args()); } /** diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php new file mode 100644 index 000000000000..69ac9409237c --- /dev/null +++ b/src/Illuminate/Cache/RedisTaggedCache.php @@ -0,0 +1,31 @@ +taggedItemKey($key); + + $this->store->connection()->lpush($this->foreverKey(), $key); + + $this->store->forever($this->taggedItemKey($key), $value); + } + + /** + * Remove all items from the cache. + * + * @return void + */ + public function flush() + { + // + } + +} \ No newline at end of file diff --git a/src/Illuminate/Cache/TagSet.php b/src/Illuminate/Cache/TagSet.php index 995c209f0955..b6ee05ec3733 100644 --- a/src/Illuminate/Cache/TagSet.php +++ b/src/Illuminate/Cache/TagSet.php @@ -1,6 +1,9 @@ names = $names; $this->store = $store; + $this->names = $names; } /** - * Reset all tags + * Reset all tags in the set. * - * @return string + * @return void */ public function reset() { - foreach($this->names as $name) - $this->resetTag($name); + array_walk($this->names, array($this, 'resetTag')); } /** - * Get the unique tag identifier. + * Get the unique tag identifier for a given tag. * - * @param string the name for a given tag + * @param string $name * @return string */ - protected function tagId($name) + public function tagId($name) { - $id = $this->store->get($this->tagKey($name)); - - if (is_null($id)) - { - $id = $this->resetTag($name); - } - - return $id; + return $this->store->get($this->tagKey($name)) ?: $this->resetTag($name); } /** - * get an array of tag identifiers for all tags + * Get an array of tag identifiers for all of the tags in the set. + * * @return array */ protected function tagIds() { - $ids = array(); - foreach ($this->names as $name) - $ids[] = $this->tagId($name); - return $ids; + return array_map(array($this, 'tagId'), $this->names); } /** - * get a unique namespace that will change if any of the tags are flushed + * Get a unique namespace that changes when any of the tags are flushed. + * * @return string */ public function getNamespace() { - //The sha1 ensures that the namespace is not too long, but is otherwise unnecessary - return sha1(implode('|', $this->tagIds())); + return implode('|', $this->tagIds()); } /** - * Reset the tag, returning a new tag identifier + * Reset the tag and return the new tag identifier * - * @param string $name + * @param string $name * @return string */ - protected function resetTag($name) + public function resetTag($name) { $this->store->forever($this->tagKey($name), $id = uniqid()); @@ -96,7 +89,7 @@ protected function resetTag($name) /** * Get the tag identifier key for a given tag. * - * @param string $name + * @param string $name * @return string */ public function tagKey($name) @@ -104,4 +97,14 @@ public function tagKey($name) return 'tag:'.$name.':key'; } + /** + * Get a traversable implementation for the class. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->names); + } + } \ No newline at end of file From 6c1135d72a85885cd5684f49cb4172df731247ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Nov 2013 13:31:43 -0500 Subject: [PATCH 7/9] Work on Redis cache tagging. --- src/Illuminate/Cache/RedisSection.php | 105 ---------------------- src/Illuminate/Cache/RedisTaggedCache.php | 71 +++++++++++++-- src/Illuminate/Cache/TaggedCache.php | 2 +- tests/Cache/CacheTaggedCacheTest.php | 3 +- 4 files changed, 69 insertions(+), 112 deletions(-) delete mode 100755 src/Illuminate/Cache/RedisSection.php diff --git a/src/Illuminate/Cache/RedisSection.php b/src/Illuminate/Cache/RedisSection.php deleted file mode 100755 index 2d2a7e1c5b4b..000000000000 --- a/src/Illuminate/Cache/RedisSection.php +++ /dev/null @@ -1,105 +0,0 @@ -name = $name; - } - - /** - * Store an item in the cache indefinitely. - * - * @param string $key - * @param mixed $value - * @return void - */ - public function forever($key, $value) - { - $this->store->connection()->lpush($this->foreverKey(), $key); - - parent::forever($key, $value); - } - - /** - * Remove all items from the cache. - * - * @return void - */ - public function flush() - { - $this->deleteForeverKeys(); - - $this->store->connection()->del($this->foreverKey()); - - parent::flush(); - } - - /** - * Delete all of the keys that have been stored forever. - * - * @return void - */ - protected function deleteForeverKeys() - { - $forever = $this->getForeverKeys(); - - if (count($forever) > 0) - { - call_user_func_array(array($this->store->connection(), 'del'), $forever); - } - } - - /** - * Get the keys that have been stored forever. - * - * @return array - */ - protected function getForeverKeys() - { - $me = $this; - - return array_map(function($x) use ($me) - { - return $me->getPrefix().$me->taggedItemKey($x); - - }, array_unique($this->store->connection()->lrange($this->foreverKey(), 0, -1))); - } - - /** - * Get the forever list identifier. - * - * @return string - */ - protected function foreverKey() - { - return $this->tags->tagKey($this->name).':forever'; - } - - /** - * Get the cache key prefix. - * - * @return string - */ - public function getPrefix() - { - return $this->store->getPrefix(); - } - -} \ No newline at end of file diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php index 69ac9409237c..cf02951b713a 100644 --- a/src/Illuminate/Cache/RedisTaggedCache.php +++ b/src/Illuminate/Cache/RedisTaggedCache.php @@ -11,11 +11,9 @@ class RedisTaggedCache extends TaggedCache { */ public function forever($key, $value) { - $key = $this->taggedItemKey($key); + $this->pushForeverKeys($namespace = $this->tags->getNamespace(), $key); - $this->store->connection()->lpush($this->foreverKey(), $key); - - $this->store->forever($this->taggedItemKey($key), $value); + $this->store->forever(sha1($namespace).':'.$key, $value); } /** @@ -25,7 +23,70 @@ public function forever($key, $value) */ public function flush() { - // + $this->deleteForeverKeys(); + + parent::flush(); + } + + /** + * Store a copy of the full key for each namespace segment. + * + * @param string $namespace + * @param string $key + * @return void + */ + protected function pushForeverKeys($namespace, $key) + { + $fullKey = $this->getPrefix().sha1($namespace).':'.$key; + + foreach (explode('|', $namespace) as $segment) + { + $this->store->connection()->lpush($this->foreverKey($segment), $fullKey); + } + } + + /** + * Delete all of the items that were stored forever. + * + * @return void + */ + protected function deleteForeverKeys() + { + $namespace = $this->tags->getNamespace(); + + foreach (explode('|', $namespace) as $segment) + { + $this->deleteForeverValues($segment = $this->foreverKey($segment)); + + $this->store->connection()->del($segment); + } + } + + /** + * Delete all of the keys that have been stored forever. + * + * @param string $foreverKey + * @return void + */ + protected function deleteForeverValues($foreverKey) + { + $forever = array_unique($this->store->connection()->lrange($foreverKey, 0, -1)); + + if (count($forever) > 0) + { + call_user_func_array(array($this->store->connection(), 'del'), $forever); + } + } + + /** + * Get the forever reference key for hte segment. + * + * @param string $segment + * @return string + */ + protected function foreverKey($segment) + { + return $this->getPrefix().':'.$segment.':forever'; } } \ No newline at end of file diff --git a/src/Illuminate/Cache/TaggedCache.php b/src/Illuminate/Cache/TaggedCache.php index 890958564df8..0749b1ba68d9 100644 --- a/src/Illuminate/Cache/TaggedCache.php +++ b/src/Illuminate/Cache/TaggedCache.php @@ -185,7 +185,7 @@ public function rememberForever($key, Closure $callback) */ public function taggedItemKey($key) { - return $this->tags->getNamespace().':'.$key; + return $this->getPrefix().sha1($this->tags->getNamespace()).':'.$key; } /** diff --git a/tests/Cache/CacheTaggedCacheTest.php b/tests/Cache/CacheTaggedCacheTest.php index 6dc53309ccf8..42ab87ebec38 100644 --- a/tests/Cache/CacheTaggedCacheTest.php +++ b/tests/Cache/CacheTaggedCacheTest.php @@ -34,5 +34,6 @@ public function testCacheSavedWithMultipleTagsCanBeFlushed() $store->tag('zap')->flush(); $this->assertNull($store->tags($tags1)->get('foo')); $this->assertEquals('bar', $store->tags($tags2)->get('foo')); - } + } + } \ No newline at end of file From 108ded1ad34bf95ae20f5105663e913e9f7484f9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Nov 2013 13:56:21 -0500 Subject: [PATCH 8/9] Tests for Redis cache tagging. --- src/Illuminate/Cache/RedisTaggedCache.php | 6 ++-- src/Illuminate/Cache/TaggableStore.php | 2 +- src/Illuminate/Cache/TaggedCache.php | 10 +++--- tests/Cache/CacheTaggedCacheTest.php | 43 +++++++++++++++++++++++ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php index cf02951b713a..093b1da57b95 100644 --- a/src/Illuminate/Cache/RedisTaggedCache.php +++ b/src/Illuminate/Cache/RedisTaggedCache.php @@ -52,9 +52,7 @@ protected function pushForeverKeys($namespace, $key) */ protected function deleteForeverKeys() { - $namespace = $this->tags->getNamespace(); - - foreach (explode('|', $namespace) as $segment) + foreach (explode('|', $this->tags->getNamespace()) as $segment) { $this->deleteForeverValues($segment = $this->foreverKey($segment)); @@ -86,7 +84,7 @@ protected function deleteForeverValues($foreverKey) */ protected function foreverKey($segment) { - return $this->getPrefix().':'.$segment.':forever'; + return $this->getPrefix().$segment.':forever'; } } \ No newline at end of file diff --git a/src/Illuminate/Cache/TaggableStore.php b/src/Illuminate/Cache/TaggableStore.php index e27d3bfc6c31..d640d6b4e254 100644 --- a/src/Illuminate/Cache/TaggableStore.php +++ b/src/Illuminate/Cache/TaggableStore.php @@ -32,7 +32,7 @@ public function tag($name) */ public function tags($names) { - return new TaggedCache($this, is_array($names) ? $names : func_get_args()); + return new TaggedCache($this, new TagSet($this, is_array($names) ? $names : func_get_args())); } } diff --git a/src/Illuminate/Cache/TaggedCache.php b/src/Illuminate/Cache/TaggedCache.php index 0749b1ba68d9..2f3daa5ccb7f 100644 --- a/src/Illuminate/Cache/TaggedCache.php +++ b/src/Illuminate/Cache/TaggedCache.php @@ -12,9 +12,9 @@ class TaggedCache implements StoreInterface { protected $store; /** - * The tag names. + * The tag set instance. * - * @var array + * @var \Illuminate\Cache\TagSet */ protected $tags; @@ -22,13 +22,13 @@ class TaggedCache implements StoreInterface { * Create a new tagged cache instance. * * @param \Illuminate\Cache\StoreInterface $store - * @param string $names + * @param \Illuminate\Cache\TagSet $tags * @return void */ - public function __construct(StoreInterface $store, $names) + public function __construct(StoreInterface $store, TagSet $tags) { + $this->tags = $tags; $this->store = $store; - $this->tags = new TagSet($store, $names); } /** diff --git a/tests/Cache/CacheTaggedCacheTest.php b/tests/Cache/CacheTaggedCacheTest.php index 42ab87ebec38..2a930743ef6f 100644 --- a/tests/Cache/CacheTaggedCacheTest.php +++ b/tests/Cache/CacheTaggedCacheTest.php @@ -1,9 +1,16 @@ assertEquals('bar', $store->tags($tags2)->get('foo')); } + + public function testRedisCacheTagsPushForeverKeysCorrectly() + { + $store = m::mock('Illuminate\Cache\StoreInterface'); + $tagSet = m::mock('Illuminate\Cache\TagSet', array($store, array('foo', 'bar'))); + $tagSet->shouldReceive('getNamespace')->andReturn('foo|bar'); + $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); + $store->shouldReceive('getPrefix')->andReturn('prefix:'); + $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); + $conn->shouldReceive('lpush')->once()->with('prefix:foo:forever', 'prefix:'.sha1('foo|bar').':key1'); + $conn->shouldReceive('lpush')->once()->with('prefix:bar:forever', 'prefix:'.sha1('foo|bar').':key1'); + $store->shouldReceive('forever')->with(sha1('foo|bar').':key1', 'key1:value'); + + $redis->forever('key1', 'key1:value'); + } + + + public function testRedisCacheForeverTagsCanBeFlushed() + { + $store = m::mock('Illuminate\Cache\StoreInterface'); + $tagSet = m::mock('Illuminate\Cache\TagSet', array($store, array('foo', 'bar'))); + $tagSet->shouldReceive('getNamespace')->andReturn('foo|bar'); + $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); + $store->shouldReceive('getPrefix')->andReturn('prefix:'); + $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); + $conn->shouldReceive('lrange')->once()->with('prefix:foo:forever', 0, -1)->andReturn(array('key1', 'key2')); + $conn->shouldReceive('lrange')->once()->with('prefix:bar:forever', 0, -1)->andReturn(array('key3')); + $conn->shouldReceive('del')->once()->with('key1', 'key2'); + $conn->shouldReceive('del')->once()->with('key3'); + $conn->shouldReceive('del')->once()->with('prefix:foo:forever'); + $conn->shouldReceive('del')->once()->with('prefix:bar:forever'); + $tagSet->shouldReceive('reset')->once(); + + $redis->flush(); + } + } \ No newline at end of file From 1b4d4f2bb9852bc9817766188c4b3ef2780809d1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Nov 2013 13:57:10 -0500 Subject: [PATCH 9/9] Update change log. --- src/Illuminate/Foundation/changes.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/changes.json b/src/Illuminate/Foundation/changes.json index 70cbe5836d8f..2710c2f7fc3f 100755 --- a/src/Illuminate/Foundation/changes.json +++ b/src/Illuminate/Foundation/changes.json @@ -42,7 +42,8 @@ {"message": "Eloquent 'has' method will now maintain where clauses set on relation.", "backport": null}, {"message": "New 'whereHas' and 'orWhereHas' Eloquent methods that allow extra constraints on 'has' type queries.", "backport": null}, {"message": "New 'or' syntax in Blade echos can be used to build isset statements and echos.", "backport": null}, - {"message": "Allow the 'name' of belongsTo and belongsToMany to be explictly set.", "backport": null} + {"message": "Allow the 'name' of belongsTo and belongsToMany to be explictly set.", "backport": null}, + {"message": "New Cache::tags feature that allows tagging cached items and flushing them by any tag.", "backport": null} ], "4.0.x": [ {"message": "Added implode method to query builder and Collection class.", "backport": null},