Skip to content

Commit

Permalink
Serialization option added to the attribute.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmatosbc committed Dec 13, 2024
1 parent 040ff3c commit 1be8734
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 31 deletions.
36 changes: 35 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Mnemosyne is a powerful and flexible caching library for PHP 8.0+ that uses attr
- Cache tags for group invalidation
- PSR-16 (SimpleCache) compatibility
- Flexible cache key templates
- Smart serialization handling:
- Automatic serialization of complex objects
- Optional raw storage for simple data types
- Full control over serialization behavior

## Installation

Expand All @@ -22,6 +26,11 @@ composer require cmatosbc/mnemosyne

## Usage

To use Mnemosyne in your classes, you must:
1. Use the `CacheTrait` in your class
2. Inject a PSR-16 compatible cache implementation
3. Apply the `Cache` attribute to methods you want to cache

### Basic Usage

```php
Expand All @@ -31,7 +40,7 @@ use Psr\SimpleCache\CacheInterface;

class UserService
{
use CacheTrait;
use CacheTrait; // Required to enable caching functionality

public function __construct(CacheInterface $cache)
{
Expand All @@ -52,6 +61,31 @@ class UserService
}
```

### Serialization Control

The `Cache` attribute allows you to control how values are stored in cache:

```php
class UserService
{
use CacheTrait;

// Automatically serialize complex objects
#[Cache(key: 'user:{id}', ttl: 3600, serialize: true)]
public function getUser(int $id): User
{
return $this->cacheCall('doGetUser', func_get_args());
}

// Store simple arrays without serialization
#[Cache(key: 'users:list', ttl: 3600, serialize: false)]
public function getUsersList(): array
{
return $this->cacheCall('doGetUsersList', func_get_args());
}
}
```

### Custom Cache Keys

```php
Expand Down
48 changes: 20 additions & 28 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ class Cache
* @param int|null $ttl Time-to-live in seconds. If null, cache will not expire.
* @param array $invalidates List of cache key templates to invalidate when this method is called.
* @param array $tags List of tags to associate with this cache entry. Supports parameter interpolation.
* @param bool|null $serialize Whether to serialize the result before caching. If null, will be determined automatically.
*/
public function __construct(
public ?string $key = null,
public ?int $ttl = null,
public array $invalidates = [],
public array $tags = [],
public ?bool $serialize = null,
) {
}
}
5 changes: 3 additions & 2 deletions src/CacheTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private function cacheCall(string $method, array $args)
if ($key !== null) {
$result = $this->cache->get($key);
if ($result !== null) {
return $result;
return $config->serialize ? unserialize($result) : $result;
}
}

Expand All @@ -108,7 +108,8 @@ private function cacheCall(string $method, array $args)

// Cache the result if we have a key
if ($key !== null) {
$this->cache->set($key, $result, $config->ttl);
$valueToCache = $config->serialize ? serialize($result) : $result;
$this->cache->set($key, $valueToCache, $config->ttl);

// Store tags if any are defined
if (!empty($config->tags)) {
Expand Down
51 changes: 51 additions & 0 deletions tests/CacheAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,55 @@ public function testCacheOperations(): void
$this->assertEquals('user:42', $operations[1]['key']);
$this->assertEquals(3600, $operations[1]['ttl']);
}

public function testSerializationEnabled(): void
{
echo "\nTest: Method With Serialization\n";
echo "----------------------------\n";

// First call should cache the serialized object
echo "First call (should miss cache and store serialized result):\n";
$result1 = $this->service->methodWithSerialization(42);
echo "Result: " . json_encode($result1) . "\n";

// Verify the cached value is serialized
$cachedValue = $this->cache->get('complex:42');
$this->assertTrue(is_string($cachedValue));
$this->assertStringStartsWith('O:8:"stdClass":', $cachedValue);

// Second call should unserialize from cache
echo "\nSecond call (should hit cache and unserialize):\n";
$result2 = $this->service->methodWithSerialization(42);
echo "Result: " . json_encode($result2) . "\n";

// Verify both results are identical objects
$this->assertEquals($result1->id, $result2->id);
$this->assertEquals($result1->count, $result2->count);
$this->assertEquals($result1->timestamp, $result2->timestamp);
$this->assertEquals(1, $this->service->getCounter());
}

public function testSerializationDisabled(): void
{
echo "\nTest: Method Without Serialization\n";
echo "--------------------------------\n";

// First call should cache the raw array
echo "First call (should miss cache and store raw result):\n";
$result1 = $this->service->methodWithoutSerialization(42);
echo "Result: " . json_encode($result1) . "\n";

// Verify the cached value is not serialized
$cachedValue = $this->cache->get('noserialize:42');
$this->assertIsArray($cachedValue);
$this->assertEquals($result1, $cachedValue);

// Second call should return the raw cached array
echo "\nSecond call (should hit cache):\n";
$result2 = $this->service->methodWithoutSerialization(42);
echo "Result: " . json_encode($result2) . "\n";

$this->assertEquals($result1, $result2);
$this->assertEquals(1, $this->service->getCounter());
}
}
26 changes: 26 additions & 0 deletions tests/Fixtures/TestService.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,32 @@ private function doComplexMethod(int $id, int $deptId): array
return ['id' => $id, 'deptId' => $deptId, 'count' => ++$this->counter];
}

#[Cache(key: 'complex:{id}', ttl: 3600, serialize: true)]
public function methodWithSerialization(int $id): \stdClass
{
return $this->cacheCall('doMethodWithSerialization', func_get_args());
}

private function doMethodWithSerialization(int $id): \stdClass
{
$obj = new \stdClass();
$obj->id = $id;
$obj->count = ++$this->counter;
$obj->timestamp = time();
return $obj;
}

#[Cache(key: 'noserialize:{id}', ttl: 3600, serialize: false)]
public function methodWithoutSerialization(int $id): array
{
return $this->cacheCall('doMethodWithoutSerialization', func_get_args());
}

private function doMethodWithoutSerialization(int $id): array
{
return ['id' => $id, 'count' => ++$this->counter];
}

public function manualInvalidation(int $id): void
{
$this->invalidateCache("user:$id");
Expand Down

0 comments on commit 1be8734

Please sign in to comment.