-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
291 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
{"config":{"phpVersion":80226,"phpExtensions":"74167a1a9fe1ad09c87a54206ee3fec6","tabWidth":4,"encoding":"utf-8","recordErrors":true,"annotations":true,"configData":[],"codeHash":"b5dc58ae9d1a395f86384fd9a1678bb4","rulesetHash":"670f8348153700c3a70fc6127ca0e484"},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/Cache.php":{"hash":"07fb00eb221304d5fd7797f4d106abdb33204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":37,"81-120":6}},"Line indent":{"values":{"spaces":18}},"PHP keyword case":{"values":{"lower":9}},"Multiple statements on same line":{"values":{"no":1}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP constant case":{"values":{"lower":3}},"PHP type case":{"values":{"lower":5}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":255},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/CacheTrait.php":{"hash":"318533789daa0830ab55df82d1dcfc2433204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":182,"81-120":6}},"Line indent":{"values":{"spaces":145}},"PHP keyword case":{"values":{"lower":49}},"Multiple statements on same line":{"values":{"no":40}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP type case":{"values":{"lower":13}},"CamelCase method name":{"values":{"yes":5}},"Function opening brace placement":{"values":{"new line":5}},"Spaces after control structure open parenthesis":{"values":[15]},"Spaces before control structure close parenthesis":{"values":[15]},"Blank lines at start of control structure":{"values":[15]},"Blank lines at end of control structure":{"values":[15]},"Control structure defined inline":{"values":{"no":15}},"PHP constant case":{"values":{"lower":5}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":1480},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/CacheAttribute.php":{"hash":"14a86252da512a54b67bd6dc8da1de0c33204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":146,"81-120":5}},"Line indent":{"values":{"spaces":136}},"PHP keyword case":{"values":{"lower":59}},"Multiple statements on same line":{"values":{"no":37}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP type case":{"values":{"lower":16}},"Constant name case":{"values":{"upper":1}},"CamelCase method name":{"values":{"yes":5}},"Function opening brace placement":{"values":{"new line":6}},"Spaces after control structure open parenthesis":{"values":[16]},"Spaces before control structure close parenthesis":{"values":[16]},"Blank lines at start of control structure":{"values":[17]},"Blank lines at end of control structure":{"values":[17]},"Control structure defined inline":{"values":{"no":15}},"PHP constant case":{"values":{"lower":6}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":1271}} | ||
{"config":{"phpVersion":80226,"phpExtensions":"74167a1a9fe1ad09c87a54206ee3fec6","tabWidth":4,"encoding":"utf-8","recordErrors":true,"annotations":true,"configData":[],"codeHash":"b5dc58ae9d1a395f86384fd9a1678bb4","rulesetHash":"670f8348153700c3a70fc6127ca0e484"},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/Cache.php":{"hash":"3dae1fc2c9aa671290655a5903f1ce2933204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":44,"81-120":6}},"Line indent":{"values":{"spaces":18}},"PHP keyword case":{"values":{"lower":9}},"Multiple statements on same line":{"values":{"no":1}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP constant case":{"values":{"lower":3}},"PHP type case":{"values":{"lower":5}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":288},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/CacheTrait.php":{"hash":"318533789daa0830ab55df82d1dcfc2433204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":182,"81-120":6}},"Line indent":{"values":{"spaces":145}},"PHP keyword case":{"values":{"lower":49}},"Multiple statements on same line":{"values":{"no":40}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP type case":{"values":{"lower":13}},"CamelCase method name":{"values":{"yes":5}},"Function opening brace placement":{"values":{"new line":5}},"Spaces after control structure open parenthesis":{"values":[15]},"Spaces before control structure close parenthesis":{"values":[15]},"Blank lines at start of control structure":{"values":[15]},"Blank lines at end of control structure":{"values":[15]},"Control structure defined inline":{"values":{"no":15}},"PHP constant case":{"values":{"lower":5}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":1480},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/CacheAttribute.php":{"hash":"14a86252da512a54b67bd6dc8da1de0c33204","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":146,"81-120":5}},"Line indent":{"values":{"spaces":136}},"PHP keyword case":{"values":{"lower":59}},"Multiple statements on same line":{"values":{"no":37}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP type case":{"values":{"lower":16}},"Constant name case":{"values":{"upper":1}},"CamelCase method name":{"values":{"yes":5}},"Function opening brace placement":{"values":{"new line":6}},"Spaces after control structure open parenthesis":{"values":[16]},"Spaces before control structure close parenthesis":{"values":[16]},"Blank lines at start of control structure":{"values":[17]},"Blank lines at end of control structure":{"values":[17]},"Control structure defined inline":{"values":{"no":15}},"PHP constant case":{"values":{"lower":6}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":1271},"\/home\/carlos\/Projects\/test\/mnemosyne\/src\/CacheManager.php":{"hash":"cfbaee9751707b6990f3b6b9413ace7933188","errors":[],"warnings":[],"metrics":{"Declarations and side effects mixed":{"values":{"no":1}},"PHP short open tag used":{"values":{"no":1}},"EOL char":{"values":{"\\n":1}},"Number of newlines at EOF":{"values":{"1":1}},"PHP closing tag at end of PHP-only file":{"values":{"no":1}},"Line length":{"values":{"80 or less":71,"81-120":5}},"Line indent":{"values":{"spaces":63}},"PHP keyword case":{"values":{"lower":42}},"Multiple statements on same line":{"values":{"no":17}},"One class per file":{"values":{"yes":1}},"Class defined in namespace":{"values":{"yes":1}},"PascalCase class name":{"values":{"yes":1}},"Class opening brace placement":{"values":{"new line":1}},"PHP constant case":{"values":{"lower":2}},"CamelCase method name":{"values":{"yes":4}},"PHP type case":{"values":{"lower":7}},"Function opening brace placement":{"values":{"new line":4}},"Spaces after control structure open parenthesis":{"values":[5]},"Spaces before control structure close parenthesis":{"values":[5]},"Blank lines at start of control structure":{"values":[5]},"Blank lines at end of control structure":{"values":[5]},"Control structure defined inline":{"values":{"no":5}}},"errorCount":0,"warningCount":0,"fixableCount":0,"numTokens":557}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
namespace Mnemosyne; | ||
|
||
use Psr\SimpleCache\CacheInterface; | ||
|
||
/** | ||
* Manages caching for functions. | ||
* | ||
* This class provides caching functionality for functions using a wrapper approach. | ||
* It uses a PSR-16 compatible cache implementation and handles cache key generation, | ||
* storage, and retrieval. | ||
*/ | ||
class CacheManager | ||
{ | ||
private static ?CacheInterface $cache = null; | ||
|
||
/** | ||
* Set the cache implementation to use. | ||
*/ | ||
public static function setCache(CacheInterface $cache): void | ||
{ | ||
self::$cache = $cache; | ||
} | ||
|
||
/** | ||
* Get the current cache implementation. | ||
*/ | ||
public static function getCache(): ?CacheInterface | ||
{ | ||
return self::$cache; | ||
} | ||
|
||
/** | ||
* Wrap a function call with caching behavior. | ||
* | ||
* @param callable $function The function to cache | ||
* @param array $params Named parameters for key interpolation | ||
* @param Cache $attribute The cache attribute instance | ||
* @return mixed The cached or computed result | ||
*/ | ||
public static function wrap(callable $function, array $params, Cache $attribute): mixed | ||
{ | ||
if (!self::$cache) { | ||
throw new \RuntimeException('Cache implementation not set. Call CacheManager::setCache() first.'); | ||
} | ||
|
||
$key = self::resolveCacheKey($attribute->key, $params); | ||
|
||
// Check if value is already cached | ||
if (self::$cache->has($key)) { | ||
return self::$cache->get($key); | ||
} | ||
|
||
// Call the original function | ||
$result = $function(); | ||
|
||
// Cache the result | ||
self::$cache->set($key, $result, $attribute->ttl); | ||
|
||
// Handle tags if present | ||
if (!empty($attribute->tags)) { | ||
foreach ($attribute->tags as $tag) { | ||
$tagKey = "tag:$tag"; | ||
$keys = self::$cache->get($tagKey, []); | ||
$keys[] = $key; | ||
self::$cache->set($tagKey, array_unique($keys)); | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
/** | ||
* Resolve a cache key template using parameters. | ||
*/ | ||
private static function resolveCacheKey(?string $template, array $params): string | ||
{ | ||
if ($template === null) { | ||
// Generate automatic key based on parameters | ||
return md5(serialize($params)); | ||
} | ||
|
||
return preg_replace_callback( | ||
'/{([^}]+)}/', | ||
fn($m) => $params[$m[1]] ?? '', | ||
$template | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<?php | ||
|
||
namespace Mnemosyne\Tests; | ||
|
||
use Mnemosyne\Cache; | ||
use Mnemosyne\CacheManager; | ||
use Mnemosyne\Tests\Fixtures\MockCache; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class CacheFunctionTest extends TestCase | ||
{ | ||
private MockCache $cache; | ||
private static int $fibonacciCalls = 0; | ||
private static int $greetCalls = 0; | ||
|
||
protected function setUp(): void | ||
{ | ||
$this->cache = new MockCache(); | ||
CacheManager::setCache($this->cache); | ||
self::$fibonacciCalls = 0; | ||
self::$greetCalls = 0; | ||
} | ||
|
||
private function fibonacci(int $n): int | ||
{ | ||
self::$fibonacciCalls++; | ||
return $n <= 1 ? $n : $this->fibonacci($n - 1) + $this->fibonacci($n - 2); | ||
} | ||
|
||
private function greet(string $name): string | ||
{ | ||
self::$greetCalls++; | ||
return "Hello, $name!"; | ||
} | ||
|
||
public function testFibonacciCaching(): void | ||
{ | ||
echo "\nTest: Fibonacci Function Caching\n"; | ||
echo "------------------------------\n"; | ||
|
||
$cachedFib = function(int $n) { | ||
return CacheManager::wrap( | ||
fn() => $this->fibonacci($n), | ||
['n' => $n], | ||
new Cache(key: 'fibonacci:{n}', ttl: 3600) | ||
); | ||
}; | ||
|
||
// First call should compute and cache | ||
echo "First call (should miss cache and compute):\n"; | ||
$result1 = $cachedFib(10); | ||
$initialCalls = self::$fibonacciCalls; | ||
echo "Result: $result1\n"; | ||
echo "Function calls: " . self::$fibonacciCalls . "\n"; | ||
echo "Cache operations: " . count($this->cache->operations) . "\n"; | ||
|
||
// Second call should use cache | ||
echo "\nSecond call (should hit cache):\n"; | ||
$result2 = $cachedFib(10); | ||
echo "Result: $result2\n"; | ||
echo "Additional function calls: " . (self::$fibonacciCalls - $initialCalls) . "\n"; | ||
echo "Total cache operations: " . count($this->cache->operations) . "\n"; | ||
|
||
$this->assertEquals($result1, $result2); | ||
$this->assertEquals(55, $result1); // Known fibonacci(10) result | ||
$this->assertEquals($initialCalls, self::$fibonacciCalls, 'Function should not be called again when cached'); | ||
} | ||
|
||
public function testGreetingWithTags(): void | ||
{ | ||
echo "\nTest: Greeting Function with Tags\n"; | ||
echo "-------------------------------\n"; | ||
|
||
$cachedGreet = function(string $name) { | ||
return CacheManager::wrap( | ||
fn() => $this->greet($name), | ||
['name' => $name], | ||
new Cache(key: 'greet:{name}', tags: ['greetings']) | ||
); | ||
}; | ||
|
||
// First call | ||
echo "First call with name 'John':\n"; | ||
$result1 = $cachedGreet('John'); | ||
$initialCalls = self::$greetCalls; | ||
echo "Result: $result1\n"; | ||
echo "Function calls: " . self::$greetCalls . "\n"; | ||
echo "Cache operations: " . count($this->cache->operations) . "\n"; | ||
|
||
// Second call | ||
echo "\nSecond call with same name:\n"; | ||
$result2 = $cachedGreet('John'); | ||
echo "Result: $result2\n"; | ||
echo "Additional function calls: " . (self::$greetCalls - $initialCalls) . "\n"; | ||
echo "Total cache operations: " . count($this->cache->operations) . "\n"; | ||
|
||
$this->assertEquals('Hello, John!', $result1); | ||
$this->assertEquals($initialCalls, self::$greetCalls, 'Function should not be called again when cached'); | ||
|
||
// Verify tag was stored | ||
$this->assertTrue( | ||
$this->cache->has('tag:greetings'), | ||
'Tag should be stored in cache' | ||
); | ||
|
||
echo "\nTag verification:\n"; | ||
echo "Tag 'greetings' exists in cache: " . ($this->cache->has('tag:greetings') ? 'yes' : 'no') . "\n"; | ||
} | ||
} |