Skip to content

Commit 2e126a2

Browse files
committed
Merge pull request #9 from Nyholm/no_gen
We do not have to gerenerate a new key if we do not use tags
2 parents ce23b3c + 852e302 commit 2e126a2

8 files changed

Lines changed: 224 additions & 21 deletions

README.md

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,17 @@ $pool->clear();
5656
If you are writing a PSR-6 implementation you may want to use this library. The implementation is easy and will work
5757
with all PSR-6 caches.
5858

59-
**Warning: All the cache keys will change because we have to generate new cache keys that
60-
depends on the tags. This will include keys that do not use tags.**
61-
62-
You need to do three changes on the implementation of `CacheItemPoolInterface`.
59+
You need to do a few changes on the implementation of `CacheItemPoolInterface`.
6360

6461
* Implement `TaggablePoolInterface` and use `TaggablePoolTrait`
62+
* Implement `TaggableItemInterface` and use `TaggableItemTrait`
6563
* Use `TaggablePoolTrait::generateCacheKey($key, array $tags)`
66-
* Implement `CachePool::getTagItem($key)`
64+
* Use `TaggableItemInterface::getTaggedKey()`
65+
* Implement `CachePool::getItemWithoutGenerateCacheKey($key)`
66+
* Implement `CachePool::validateTagName($key)`
6767

6868

69-
### Implement interface and use trait
69+
### Implement interface and use trait for CacheItemPoolInterface
7070

7171
The trait has two protected methods; `generateCacheKey($key, array $tags)` and `flushTag($name)`.
7272

@@ -79,6 +79,34 @@ class Pool implements CacheItemPoolInterface, TaggablePoolInterface
7979
}
8080
```
8181

82+
### Implement interface and use trait for CacheItemInterface
83+
84+
The purpose of the trait is to be able to return a `taggedKey` and a normal `key`. The trait has one protected function
85+
`getKeyFromTaggedKey()` and one public function `getTaggedkey()`. You should use the trait and make sure you set values
86+
to your two keys.
87+
88+
```php
89+
class Pool implements CacheItemInterface, TaggableItemInterface
90+
{
91+
use TaggableItemTrait;
92+
93+
private $normalKey;
94+
95+
public function __construct($key)
96+
{
97+
$this->taggedKey = $key;
98+
$this->normalKey = $this->getKeyFromTaggedKey($key);
99+
}
100+
101+
public function getKey()
102+
{
103+
return $this->normalKey;
104+
}
105+
106+
// ...
107+
}
108+
```
109+
82110
### Generate cache key
83111

84112
Your cache pool's `getItem()` probably look like this:
@@ -129,10 +157,24 @@ Here is the list of functions you need to change:
129157
* deleteItem
130158
* deleteItems
131159

132-
### Implement CachePool::getTagItem($key)
160+
### Use TaggableItemInterface::getTaggedKey()
161+
162+
To make sure we fetch the correct item the cache pool should always use `$item->getTaggedKey`. This will return a
163+
cache key that depends on the tags.
164+
165+
```php
166+
public function save(CacheItemInterface $item)
167+
{
168+
$key = $item->getTaggedKey();
169+
170+
return $this->storage->save($key, $item);
171+
}
172+
```
173+
174+
### Implement CachePool::getItemWithoutGenerateCacheKey($key)
133175

134176
The trait uses the cache as a key-value store. The key is the tag name and the value is a random id created by
135-
`uniqid()`. The way to access the cache is by a protected function `getTagItem($key)`. This function will be very similar to your
177+
`uniqid()`. The way to access the cache is by a protected function `getItemWithoutGenerateCacheKey($key)`. This function will be very similar to your
136178
`getItem($key, array $tags = [])`. The only difference is that the latter will call `generateCacheKey()`.
137179

138180
Consider your new `getItem($key, array $tags = [])`:
@@ -150,9 +192,9 @@ public function getItem($key, array $tags = [])
150192
}
151193
```
152194

153-
You would need `getTagItem($key)` to look like this:
195+
You would need `getItemWithoutGenerateCacheKey($key)` to look like this:
154196
```php
155-
protected function getTagItem($key)
197+
protected function getItemWithoutGenerateCacheKey($key)
156198
{
157199
$item = $this->storage->fetch($key);
158200
if (false === $item) {
@@ -169,10 +211,10 @@ public function getItem($key, array $tags = [])
169211
{
170212
$taggedKey = $this->generateCacheKey($key, $tags);
171213

172-
return $this->getTagItem($taggedKey);
214+
return $this->getItemWithoutGenerateCacheKey($taggedKey);
173215
}
174216

175-
protected function getTagItem($key)
217+
protected function getItemWithoutGenerateCacheKey($key)
176218
{
177219
$item = $this->storage->fetch($key);
178220
if (false === $item) {
@@ -183,6 +225,23 @@ protected function getTagItem($key)
183225
}
184226
```
185227

228+
### Implement CachePool::validateTagName($key)
229+
230+
We want to make sure the user do not try to use any invalid characters in the tags. The validation could be the same
231+
as for normal keys.
232+
233+
```php
234+
public function validateTagName($name)
235+
{
236+
$this->validateKey($name);
237+
}
238+
239+
public function validateKey($name)
240+
{
241+
// The function you are using to verify the key name for normal items.
242+
}
243+
244+
```
186245

187246
### Deleting tagged items
188247

@@ -209,3 +268,9 @@ The `TaggablePoolTrait::flushTag($name)` changes the tag cache key so next time
209268
`TaggablePoolTrait::generateCacheKey($key, array $tags)` you will get a different cache key back. This will not remove
210269
the items from the cache, which introduce a memory leak. That is why it is important to use memcached or redis, which
211270
automatically purges stale records.
271+
272+
273+
## Test your tagging cache
274+
275+
When you are happy with your implementation you should test it. We have provided some integration tests that will test
276+
your implementation for you. See this repository for more info: https://github.com/php-cache/integration-tests

src/TaggableItemInterface.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of php-cache\taggable-cache package.
5+
*
6+
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Cache\Taggable;
13+
14+
/**
15+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
16+
*/
17+
interface TaggableItemInterface
18+
{
19+
/**
20+
* Get the key with the tag prefix.
21+
*
22+
* @return string
23+
*/
24+
public function getTaggedKey();
25+
}

src/TaggableItemTrait.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/*
4+
* This file is part of php-cache\taggable-cache package.
5+
*
6+
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Cache\Taggable;
13+
14+
/**
15+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
16+
*/
17+
trait TaggableItemTrait
18+
{
19+
/**
20+
* @type string
21+
*/
22+
protected $taggedKey;
23+
24+
/**
25+
* A key that is dependent on the tags.
26+
*
27+
* @return string
28+
*/
29+
public function getTaggedKey()
30+
{
31+
return $this->taggedKey;
32+
}
33+
34+
/**
35+
* Return the cache key for this item. This is the generic cache key that the calling library sees.
36+
*
37+
* @return string
38+
*/
39+
protected function getKeyFromTaggedKey($taggedKey)
40+
{
41+
if (false === $pos = strrpos($taggedKey, ':')) {
42+
return $taggedKey;
43+
}
44+
45+
return substr($taggedKey, $pos + 1);
46+
}
47+
}

src/TaggablePoolTrait.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,14 @@ abstract public function getItem($key);
4747
*
4848
* @return CacheItemInterface
4949
*/
50-
abstract protected function getTagItem($key);
50+
abstract protected function getItemWithoutGenerateCacheKey($key);
51+
52+
/**
53+
* Make sure we do not use any invalid characters in the tag name. The actual tag name will be "tag:$name".
54+
*
55+
* @param string $name
56+
*/
57+
abstract protected function validateTagName($name);
5158

5259
/**
5360
* Reset the tag and return the new tag identifier.
@@ -60,7 +67,8 @@ abstract protected function getTagItem($key);
6067
*/
6168
protected function flushTag($name)
6269
{
63-
$item = $this->getTagItem($this->getTagKey($name));
70+
$this->validateTagName($name);
71+
$item = $this->getItemWithoutGenerateCacheKey($this->getTagKey($name));
6472

6573
return $this->generateNewTagId($item);
6674
}
@@ -75,6 +83,10 @@ protected function flushTag($name)
7583
*/
7684
protected function generateCacheKey($key, array $tags)
7785
{
86+
if (empty($tags)) {
87+
return $key;
88+
}
89+
7890
// We sort the tags because the order should not matter
7991
sort($tags);
8092

@@ -96,7 +108,8 @@ protected function generateCacheKey($key, array $tags)
96108
*/
97109
private function getTagId($name)
98110
{
99-
$item = $this->getTagItem($this->getTagKey($name));
111+
$this->validateTagName($name);
112+
$item = $this->getItemWithoutGenerateCacheKey($this->getTagKey($name));
100113

101114
if ($item->isHit()) {
102115
return $item->get();
@@ -114,7 +127,7 @@ private function getTagId($name)
114127
*/
115128
private function getTagKey($name)
116129
{
117-
return 'tag:'.$name.':key';
130+
return 'tag:'.$name;
118131
}
119132

120133
/**

tests/Helper/CacheItem.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111

1212
namespace Cache\Taggable\Tests\Helper;
1313

14+
use Cache\Taggable\TaggableItemInterface;
15+
use Cache\Taggable\TaggableItemTrait;
1416
use Psr\Cache\CacheItemInterface;
1517

16-
class CacheItem implements CacheItemInterface
18+
class CacheItem implements CacheItemInterface, TaggableItemInterface
1719
{
20+
use TaggableItemTrait;
21+
1822
/**
1923
* @type string
2024
*/
@@ -35,7 +39,8 @@ class CacheItem implements CacheItemInterface
3539
*/
3640
public function __construct($key)
3741
{
38-
$this->key = $key;
42+
$this->taggedKey = $key;
43+
$this->key = $this->getKeyFromTaggedKey($key);
3944
}
4045

4146
/**

tests/Helper/CachePool.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ class CachePool
2828
*/
2929
private $memoryCache;
3030

31+
protected function validateTagName($name)
32+
{
33+
}
34+
3135
public function getItem($key, array $tags = [])
3236
{
3337
$taggedKey = $this->generateCacheKey($key, $tags);
3438

35-
return $this->getTagItem($taggedKey);
39+
return $this->getItemWithoutGenerateCacheKey($taggedKey);
3640
}
3741

38-
protected function getTagItem($key)
42+
protected function getItemWithoutGenerateCacheKey($key)
3943
{
4044
if (isset($this->memoryCache[$key])) {
4145
$item = $this->memoryCache[$key];
@@ -48,7 +52,7 @@ protected function getTagItem($key)
4852

4953
public function save(CacheItemInterface $item)
5054
{
51-
$this->memoryCache[$item->getKey()] = $item;
55+
$this->memoryCache[$item->getTaggedKey()] = $item;
5256

5357
return true;
5458
}

tests/TaggableItemTraitTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of php-cache\taggable-cache package.
5+
*
6+
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Cache\Taggable\Tests;
13+
14+
use Cache\Taggable\Tests\Helper\CacheItem;
15+
16+
class TaggableItemTraitTest extends \PHPUnit_Framework_TestCase
17+
{
18+
public function testGetKey()
19+
{
20+
$item = new CacheItem('key');
21+
$this->assertEquals('key', $item->getKey());
22+
23+
$item = new CacheItem('foo:key');
24+
$this->assertEquals('key', $item->getKey());
25+
26+
$item = new CacheItem('foo:bar:key');
27+
$this->assertEquals('key', $item->getKey());
28+
}
29+
30+
public function testGetTaggedKey()
31+
{
32+
$item = new CacheItem('key');
33+
$this->assertEquals('key', $item->getTaggedKey());
34+
35+
$item = new CacheItem('foo:key');
36+
$this->assertEquals('foo:key', $item->getTaggedKey());
37+
38+
$item = new CacheItem('foo:bar:key');
39+
$this->assertEquals('foo:bar:key', $item->getTaggedKey());
40+
}
41+
}

tests/TaggablePoolTraitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public function testGenerateCacheKey()
2828
$key1 = $cache->exposeGenerateCacheKey($inputKey, ['abc', '123']);
2929
$key2 = $cache->exposeGenerateCacheKey($inputKey, ['123', 'abc']);
3030
$this->assertTrue($key1 === $key2, 'Order should not matter when generating cache keys');
31+
32+
$key1 = $cache->exposeGenerateCacheKey($inputKey, []);
33+
$this->assertTrue($inputKey === $key1, 'Key should not be altered if no tags used. ');
3134
}
3235

3336
public function testFlushTag()

0 commit comments

Comments
 (0)