Skip to content

Commit e1b391b

Browse files
authored
Merge pull request #2 from moufmouf/classBoundCache
Adding the notion of ClassBoundCache
2 parents 18334cf + 91e8644 commit e1b391b

8 files changed

Lines changed: 212 additions & 5 deletions

File tree

README.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ For instance, Doctrine Annotations in a class do not change unless the class fil
2121
sense to bind the cache invalidation to the modification date of the file. *thecodingmachine/cache-utils* provides just that.
2222

2323
```php
24-
namespace TheCodingMachine\CacheUtils\FileBoundCache;
24+
use TheCodingMachine\CacheUtils\FileBoundCache;
2525

2626
$fileBoundCache = new FileBoundCache($psr16Cache);
2727

@@ -40,8 +40,42 @@ $myDataToCache = $fileBoundCache->get('cache_key');
4040
You can also use the `MemoryAdapter` to store the cache in memory for even faster access in the same query.
4141

4242
```php
43-
namespace TheCodingMachine\CacheUtils\FileBoundCache;
44-
namespace TheCodingMachine\CacheUtils\MemoryAdapter;
43+
use TheCodingMachine\CacheUtils\FileBoundCache;
44+
use TheCodingMachine\CacheUtils\MemoryAdapter;
4545

4646
$fileBoundCache = new MemoryAdapter(new FileBoundCache($psr16Cache));
4747
```
48+
49+
### Class bound cache
50+
51+
You can also bind a cache item to a class / trait / interface using the `ClassBoundCache` class.
52+
The cache will expire if the class / trait / interface is modified.
53+
54+
```php
55+
use TheCodingMachine\CacheUtils\FileBoundCache;
56+
use TheCodingMachine\CacheUtils\ClassBoundCache;
57+
58+
$fileBoundCache = new FileBoundCache($psr16Cache);
59+
$classBoundCache = new ClassBoundCache($fileBoundCache);
60+
61+
// Put the $myDataToCache object in cache.
62+
// If the FooBar class is modified, the cache item is purged.
63+
$classBoundCache->set('cache_key', $myDataToCache, FooBar::class);
64+
65+
// Fetching data
66+
$myDataToCache = $classBoundCache->get('cache_key');
67+
```
68+
69+
The `ClassBoundCache` constructor accepts 3 additional parameters:
70+
71+
```php
72+
73+
class ClassBoundCache implements ClassBoundCacheInterface
74+
{
75+
public function __construct(FileBoundCacheInterface $fileBoundCache, bool $analyzeParentClasses = true, bool $analyzeTraits = true, bool $analyzeInterfaces = false)
76+
}
77+
```
78+
79+
- `$analyzeParentClasses`: if set to true, the cache will be invalidated if one of the parent classes is modified
80+
- `$analyzeTraits`: if set to true, the cache will be invalidated if one of the traits is modified
81+
- `$analyzeInterfaces`: if set to true, the cache will be invalidated if one of the interfaces implemented is modified

src/ClassBoundCache.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\CacheUtils;
6+
7+
use ReflectionClass;
8+
use function array_merge;
9+
10+
class ClassBoundCache implements ClassBoundCacheInterface
11+
{
12+
/** @var FileBoundCacheInterface */
13+
private $fileBoundCache;
14+
/** @var bool */
15+
private $analyzeParentClasses;
16+
/** @var bool */
17+
private $analyzeTraits;
18+
/** @var bool */
19+
private $analyzeInterfaces;
20+
21+
public function __construct(FileBoundCacheInterface $fileBoundCache, bool $analyzeParentClasses = true, bool $analyzeTraits = true, bool $analyzeInterfaces = false)
22+
{
23+
$this->fileBoundCache = $fileBoundCache;
24+
$this->analyzeParentClasses = $analyzeParentClasses;
25+
$this->analyzeTraits = $analyzeTraits;
26+
$this->analyzeInterfaces = $analyzeInterfaces;
27+
}
28+
29+
/**
30+
* Fetches an element from the cache by key.
31+
*
32+
* @return mixed
33+
*/
34+
public function get(string $key)
35+
{
36+
return $this->fileBoundCache->get($key);
37+
}
38+
39+
/**
40+
* Stores an item in the cache.
41+
*
42+
* @param mixed $item The item must be serializable.
43+
* @param ReflectionClass $refClass If the class is modified, the cache item is invalidated.
44+
*/
45+
public function set(string $key, $item, ReflectionClass $refClass, ?int $ttl = null): void
46+
{
47+
$files = $this->getFilesForClass($refClass);
48+
49+
$this->fileBoundCache->set($key, $item, $files, $ttl);
50+
}
51+
52+
/**
53+
* @return array<int, string>
54+
*/
55+
private function getFilesForClass(ReflectionClass $refClass): array
56+
{
57+
$files = [];
58+
$file = $refClass->getFileName();
59+
if ($file !== false) {
60+
$files[] = $file;
61+
}
62+
63+
if ($this->analyzeParentClasses && $refClass->getParentClass() !== false) {
64+
$files = array_merge($files, $this->getFilesForClass($refClass->getParentClass()));
65+
}
66+
67+
if ($this->analyzeTraits) {
68+
foreach ($refClass->getTraits() as $trait) {
69+
$files = array_merge($files, $this->getFilesForClass($trait));
70+
}
71+
}
72+
73+
if ($this->analyzeInterfaces) {
74+
foreach ($refClass->getInterfaces() as $interface) {
75+
$files = array_merge($files, $this->getFilesForClass($interface));
76+
}
77+
}
78+
79+
return $files;
80+
}
81+
}

src/ClassBoundCacheInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace TheCodingMachine\CacheUtils;
66

7+
use ReflectionClass;
8+
79
/**
810
* Cache items. Items expiration is bound to the modification time of a PHP class.
911
*/
@@ -20,7 +22,7 @@ public function get(string $key);
2022
* Stores an item in the cache.
2123
*
2224
* @param mixed $item The item must be serializable.
23-
* @param string $className Fully qualified class name.
25+
* @param ReflectionClass $refClass If the class is modified, the cache item is invalidated.
2426
*/
25-
public function set(string $key, $item, string $className): void;
27+
public function set(string $key, $item, ReflectionClass $refClass, ?int $ttl = null): void;
2628
}

tests/ClassBoundCacheTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace TheCodingMachine\CacheUtils;
4+
5+
use function clearstatcache;
6+
use function file_get_contents;
7+
use function file_put_contents;
8+
use ReflectionClass;
9+
use function sleep;
10+
use function str_replace;
11+
use Symfony\Component\Cache\Simple\ArrayCache;
12+
use function sys_get_temp_dir;
13+
use PHPUnit\Framework\TestCase;
14+
use TheCodingMachine\CacheUtils\Fixtures\A;
15+
use TheCodingMachine\CacheUtils\Fixtures\B;
16+
use TheCodingMachine\CacheUtils\Fixtures\C;
17+
use TheCodingMachine\CacheUtils\Fixtures\D;
18+
use function touch;
19+
20+
class ClassBoundCacheTest extends TestCase
21+
{
22+
/**
23+
* @dataProvider touchFile
24+
*/
25+
public function testFileBoundCache($classToTouch)
26+
{
27+
$cache = new ArrayCache();
28+
$fileBoundCache = new FileBoundCache($cache, 'prefix');
29+
$classBoundCache = new ClassBoundCache($fileBoundCache, true, true, true);
30+
31+
$classBoundCache->set('foo', 'bar', new ReflectionClass(A::class));
32+
33+
$this->assertSame('bar', $classBoundCache->get('foo'));
34+
35+
$classToTouch = new ReflectionClass($classToTouch);
36+
sleep(1);
37+
clearstatcache($classToTouch->getFileName());
38+
touch($classToTouch->getFileName());
39+
40+
$this->assertNull($classBoundCache->get('foo'));
41+
}
42+
43+
public function touchFile()
44+
{
45+
return [
46+
[ B::class ],
47+
[ C::class ],
48+
[ D::class ],
49+
];
50+
}
51+
}

tests/Fixtures/A.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\CacheUtils\Fixtures;
5+
6+
7+
class A extends B
8+
{
9+
10+
}

tests/Fixtures/B.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\CacheUtils\Fixtures;
5+
6+
7+
class B implements C
8+
{
9+
use D;
10+
}

tests/Fixtures/C.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\CacheUtils\Fixtures;
5+
6+
7+
interface C
8+
{
9+
10+
}

tests/Fixtures/D.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\CacheUtils\Fixtures;
5+
6+
7+
trait D
8+
{
9+
}

0 commit comments

Comments
 (0)