Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/CachedServiceGenerator/Dto/MethodCallObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Tbessenreither\MultiLevelCache\CachedServiceGenerator\Dto;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;

class MethodCallObject
{
public function __construct(
Expand Down Expand Up @@ -41,7 +43,7 @@ public function getAdditionalCacheKey(): ?string

public function getCachePrefix(): string
{
return str_replace(['\\', ':', ' '], ['_', '-', ''], $this->getClass());
return KeyGeneratorService::namespaceToKeyString($this->getClass());
}

public function getCallable(): callable
Expand Down
42 changes: 32 additions & 10 deletions src/CachedServiceGenerator/Service/InvalidatorService.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,47 @@ public function __construct(
$this->directRedisCacheService = $multiLevelCacheFactory->getImplementationRedisWithPrefix('mlc');
}

public function invalidateCacheForClass(string $classString): bool
public function invalidateEverything(): bool
{
try {
$generateKey = KeyGeneratorService::getKeyPatternForClass(new MethodCallObject($classString, '', []));
return $this->deleteByPattern('*');
}

$this->directRedisCacheService->deleteByPattern($generateKey);
public function invalidateCacheForNamespace(string $namespace): bool
{
$completedWithoutError = true;
$invalidateKeyPatterns = [
KeyGeneratorService::namespaceToKeyString($namespace, null, true) . ':*',
KeyGeneratorService::namespaceToKeyString($namespace, null, false) . ':*',
];
foreach ($invalidateKeyPatterns as $pattern) {

return true;
} catch (Throwable $e) {
return false;
$result = $this->deleteByPattern($pattern);
if ($result === false) {
$completedWithoutError = false;
}
}

return $completedWithoutError;
}

public function invalidateCacheForClass(string $classString): bool
{
$generateKey = KeyGeneratorService::getKeyPatternForClass(new MethodCallObject($classString, '', []));

return $this->deleteByPattern($generateKey);
}

public function invalidateCacheForMethod(string $classString, string $method): bool
{
try {
$generateKey = KeyGeneratorService::getKeyPatternForMethod(new MethodCallObject($classString, $method, []));
$generateKey = KeyGeneratorService::getKeyPatternForMethod(new MethodCallObject($classString, $method, []));

$this->directRedisCacheService->deleteByPattern($generateKey);
return $this->deleteByPattern($generateKey);
}

public function deleteByPattern(string $pattern): bool
{
try {
$this->directRedisCacheService->deleteByPattern($pattern);

return true;
} catch (Throwable $e) {
Expand Down
16 changes: 16 additions & 0 deletions src/CachedServiceGenerator/Service/KeyGeneratorService.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

class KeyGeneratorService
{
public const string CACHED_SERVICE_GENERATOR_KEY_PREFIX = 'CachedService';

/**
* @var array<string, string>
*/
Expand All @@ -28,6 +30,20 @@ public static function getKey(MethodCallObject $methodCallObject, bool $throw =
return self::defaultKeyGenerator($methodCallObject);
}

public static function namespaceToKeyString(string $fqcn, ?string $method = null, bool $addCsgPrefix = false): string
{
$parts = [];
if ($addCsgPrefix) {
$parts[] = self::CACHED_SERVICE_GENERATOR_KEY_PREFIX;
}
$parts[] = str_replace(['\\', ':', ' '], ['_', '-', ''], $fqcn);
if ($method) {
$parts[] = $method;
}

return implode(':', $parts);
}

public static function getKeyPatternForMethod(MethodCallObject $methodCallObject): string
{
$generatedFullKey = self::getKey($methodCallObject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class: $interfaceClass,
$classCode = RenderTemplateService::render('Class/CachedService', [
'ServiceNamespace' => $namespace,
'ServiceName' => $shortName,
'CacheKeyPrefix' => KeyGeneratorService::namespaceToKeyString(fqcn:$class, addCsgPrefix:true),
'ClassDotSeparated' => $classDotSeparated,
'ClassUnderscoreSeparated' => $classUnderscoreSeparated,
'ClassHyphenSeparated' => $classHyphenSeparated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,19 @@ use Throwable;
*
* Cache enabled wrapper for Service /*{ServiceName}*/.
*
* This Wrapper provides all public methods from the origin service and was generated using the make command
* You can regenerate/update using php vendor/tbessenreither/multi-level-cache/bin/make --service=/*{ClassDotSeparated}*/
* Or you can update all services with php vendor/tbessenreither/multi-level-cache/bin/update
* This Wrapper provides all public methods from the origin service and was generated using the make command.
* You can regenerate/update using php vendor/tbessenreither/multi-level-cache/bin/make --service=/*{ClassDotSeparated}*/.
* Or you can update all services with php vendor/tbessenreither/multi-level-cache/bin/update.
* @codeCoverageIgnore
* @mixin /*{ServiceName}*/
*/
#[MlcCachedService(
originalServiceClass: /*{ServiceName}*/::class,
allowSync: true,
)]
class /*{ServiceName}*/Cached implements /*{InterfacesString}*/
{
public const CACHE_KEY_PREFIX = 'CachedService:/*{ClassUnderscoreSeparated}*/';
public const CACHE_KEY_PREFIX = '/*{CacheKeyPrefix}*/';

/**
* @var array<string, MlcCacheableMethod|null>
Expand Down
3 changes: 3 additions & 0 deletions tests/CachedServiceGenerator/Dto/MethodCallObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

use Closure;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Dto\MethodCallObject;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;

#[CoversClass(MethodCallObject::class)]
#[UsesClass(KeyGeneratorService::class)]
class MethodCallObjectTest extends TestCase
{
public function testSetupAndGetter(): void
Expand Down
90 changes: 90 additions & 0 deletions tests/CachedServiceGenerator/Service/InvalidatorServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,53 @@
#[UsesClass(RedisClientFactory::class)]
class InvalidatorServiceTest extends TestCase
{
public function testInvalidateEverything(): void
{
$redisMock = $this->createMock(Redis::class);
$redisMock->method('isConnected')->willReturn(true);
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
['key:1', 'key:2'],
[]
);
$redisMock
->expects($this->atLeastOnce())
->method('del');

$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));

$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
->willReturn($directRedisCacheService);

$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
$result = $invalidatorService->invalidateEverything();
$this->assertTrue($result);
}

public function testInvalidateEverythingWithError(): void
{
$redisMock = $this->createMock(Redis::class);
$redisMock->method('isConnected')->willReturn(true);
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
['key:1', 'key:2'],
[]
);
$redisMock
->expects($this->atLeastOnce())
->method('del')
->willThrowException(new Exception('random error'));

$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));

$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
->willReturn($directRedisCacheService);

$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
$result = $invalidatorService->invalidateEverything();
$this->assertFalse($result);
}

public function testClassInvalidation(): void
{
$redisStub = $this->createStub(Redis::class);
Expand Down Expand Up @@ -100,4 +147,47 @@ public function testMethodInvalidationError(): void
$result = $invalidatorService->invalidateCacheForMethod(TestServiceA::class, 'testMethod');
$this->assertFalse($result);
}

public function testNamespaceInvalidation(): void
{
$redisMock = $this->createMock(Redis::class);
$redisMock->method('isConnected')->willReturn(true);
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
['key:1', 'key:2'],
[]
);
$redisMock->expects($this->atLeastOnce())
->method('del');
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));

$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
->willReturn($directRedisCacheService);

$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
$result = $invalidatorService->invalidateCacheForNamespace(TestServiceA::class);
$this->assertTrue($result);
}

public function testNamespaceInvalidationWithError(): void
{
$redisMock = $this->createMock(Redis::class);
$redisMock->method('isConnected')->willReturn(true);
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
['key:1', 'key:2'],
[]
);
$redisMock->expects($this->atLeastOnce())
->method('del')
->willThrowException(new Exception('random error'));
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));

$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
->willReturn($directRedisCacheService);

$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
$result = $invalidatorService->invalidateCacheForNamespace(TestServiceA::class);
$this->assertFalse($result);
}
}
2 changes: 2 additions & 0 deletions tests/Commands/MakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\TestCase;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;

#[CoversNothing]
class MakeTest extends TestCase
Expand Down Expand Up @@ -69,6 +70,7 @@ public function testMake(): void
$this->assertStringContainsString('public function exampleMethod(): void', $interfaceFileContent);
$this->assertStringContainsString('public function exampleMethodWithArguments(array $thing, bool $bool, int $int, string $string): void', $interfaceFileContent);

$this->assertStringContainsString("CACHE_KEY_PREFIX = '" . KeyGeneratorService::CACHED_SERVICE_GENERATOR_KEY_PREFIX . ":", $cachedFileContent);
}

public function testMakeWithInterfaces(): void
Expand Down
Loading