1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| #[Attribute(Attribute::TARGET_METHOD)] class Cache { public function __construct( public int $ttl = 3600, public array $tags = [], public ?string $key = null, public bool $enabled = true ) {} }
#[Attribute(Attribute::TARGET_METHOD)] class CacheEvict { public function __construct( public array $tags = [], public ?string $key = null ) {} }
class ProductService { #[Cache(ttl: 7200, tags: ['products', 'catalog'])] public function getProducts(string $category): array { return $this->repository->findByCategory($category); } #[Cache(ttl: 3600, key: 'product_{id}')] public function getProduct(int $id): ?Product { return $this->repository->find($id); } #[CacheEvict(tags: ['products'])] public function updateProduct(int $id, array $data): Product { return $this->repository->update($id, $data); } }
class CacheInterceptor { public function __construct(private CacheInterface $cache) {} public function intercept(object $instance, string $method, array $args): mixed { $reflection = new ReflectionMethod($instance, $method); $cacheAttrs = $reflection->getAttributes(Cache::class); if (!empty($cacheAttrs)) { $cache = $cacheAttrs[0]->newInstance(); if ($cache->enabled) { $key = $this->generateCacheKey($cache->key, $method, $args); if ($this->cache->has($key)) { return $this->cache->get($key); } $result = $instance->$method(...$args); $this->cache->set($key, $result, $cache->ttl, $cache->tags); return $result; } } $evictAttrs = $reflection->getAttributes(CacheEvict::class); if (!empty($evictAttrs)) { $result = $instance->$method(...$args); foreach ($evictAttrs as $evictAttr) { $evict = $evictAttr->newInstance(); if ($evict->key) { $this->cache->delete($evict->key); } if (!empty($evict->tags)) { $this->cache->deleteByTags($evict->tags); } } return $result; } return $instance->$method(...$args); } private function generateCacheKey(?string $template, string $method, array $args): string { if ($template) { return preg_replace_callback('/\{(\w+)\}/', function($matches) use ($args) { $param = $matches[1]; return $args[$param] ?? $matches[0]; }, $template); } return $method . '_' . md5(serialize($args)); } }
|