1<?php
2
3/**
4 * Configuration for advanced memoization
5 */
6class MemoizeConfig
7{
8 public function __construct(
9 public readonly int $maxCacheSize = 1000,
10 public readonly int $ttl = PHP_INT_MAX,
11 public readonly ?callable $cacheKeyGenerator = null
12 ) {}
13}
14
15/**
16 * Cache statistics
17 */
18class CacheStats
19{
20 public function __construct(
21 public int $hits = 0,
22 public int $misses = 0,
23 public float $totalAccessTime = 0,
24 public int $accessCount = 0
25 ) {}
26
27 public function getAverageAccessTime(): float
28 {
29 return $this->accessCount > 0 ? $this->totalAccessTime / $this->accessCount : 0;
30 }
31}
32
33/**
34 * Advanced memoization with complete cache management
35 */
36class AdvancedMemoizer
37{
38 private array $cache = [];
39 private array $timestamps = [];
40 private CacheStats $stats;
41
42 public function __construct(private readonly MemoizeConfig $config = new MemoizeConfig())
43 {
44 $this->stats = new CacheStats();
45 }
46
47 public function memoize(callable $fn): callable
48 {
49 return function (...$args) use ($fn) {
50 $startTime = microtime(true);
51 $key = $this->generateCacheKey($args);
52 $now = time();
53
54 if ($this->hasValidCacheEntry($key, $now)) {
55 $this->updateStats(true, $startTime);
56 return $this->cache[$key];
57 }
58
59 // Cache size management
60 if (count($this->cache) >= $this->config->maxCacheSize) {
61 $this->removeOldestEntry();
62 }
63
64 $result = $fn(...$args);
65 $this->cache[$key] = $result;
66 $this->timestamps[$key] = $now;
67 $this->updateStats(false, $startTime);
68
69 return $result;
70 };
71 }
72
73 private function generateCacheKey(array $args): string
74 {
75 if ($this->config->cacheKeyGenerator) {
76 return ($this->config->cacheKeyGenerator)($args);
77 }
78 return md5(serialize($args));
79 }
80
81 private function hasValidCacheEntry(string $key, int $now): bool
82 {
83 return isset($this->cache[$key]) &&
84 ($now - $this->timestamps[$key] <= $this->config->ttl);
85 }
86
87 private function updateStats(bool $isHit, float $startTime): void
88 {
89 $accessTime = microtime(true) - $startTime;
90 $this->stats->totalAccessTime += $accessTime;
91 $this->stats->accessCount++;
92
93 if ($isHit) {
94 $this->stats->hits++;
95 } else {
96 $this->stats->misses++;
97 }
98 }
99
100 private function removeOldestEntry(): void
101 {
102 $oldestKey = array_key_first($this->timestamps);
103 unset($this->cache[$oldestKey], $this->timestamps[$oldestKey]);
104 }
105
106 public function getStats(): array
107 {
108 return [
109 'hits' => $this->stats->hits,
110 'misses' => $this->stats->misses,
111 'size' => count($this->cache),
112 'averageAccessTime' => $this->stats->getAverageAccessTime()
113 ];
114 }
115
116 public function clearCache(): void
117 {
118 $this->cache = [];
119 $this->timestamps = [];
120 }
121}