| 1 |
<?php |
| 2 |
defined('ABSPATH') or die('Unauthorized Access'); |
| 3 |
|
| 4 |
class Phpinfo_WP_Object_Cache { |
| 5 |
|
| 6 |
private static function _pro(): bool { return Phpinfo_WP_License::is_valid(); } |
| 7 |
|
| 8 |
public static function has_dropin(): bool { |
| 9 |
return file_exists(WP_CONTENT_DIR . '/object-cache.php'); |
| 10 |
} |
| 11 |
|
| 12 |
public static function is_redis_extension_loaded(): bool { |
| 13 |
return extension_loaded('redis'); |
| 14 |
} |
| 15 |
|
| 16 |
public static function is_memcached_extension_loaded(): bool { |
| 17 |
return extension_loaded('memcached') || extension_loaded('memcache'); |
| 18 |
} |
| 19 |
|
| 20 |
public static function active_type(): ?string { |
| 21 |
if (!self::has_dropin()) return null; |
| 22 |
|
| 23 |
$dropin = file_get_contents(WP_CONTENT_DIR . '/object-cache.php'); |
| 24 |
if (stripos($dropin, 'redis') !== false) { |
| 25 |
return 'redis'; |
| 26 |
} |
| 27 |
if (stripos($dropin, 'memcache') !== false) { |
| 28 |
return 'memcached'; |
| 29 |
} |
| 30 |
return 'unknown'; |
| 31 |
} |
| 32 |
|
| 33 |
public static function status(): ?array { |
| 34 |
if (!self::_pro()) return null; |
| 35 |
|
| 36 |
$type = self::active_type(); |
| 37 |
if (!$type) return null; |
| 38 |
|
| 39 |
$status = [ |
| 40 |
'type' => $type, |
| 41 |
'hit_rate' => null, |
| 42 |
'hits' => 0, |
| 43 |
'misses' => 0, |
| 44 |
'memory_used' => 0, |
| 45 |
'memory_total' => 0, |
| 46 |
'memory_pct' => 0, |
| 47 |
'evictions' => 0, |
| 48 |
'keys' => 0, |
| 49 |
'connection' => 'unknown', |
| 50 |
]; |
| 51 |
|
| 52 |
global $wp_object_cache; |
| 53 |
|
| 54 |
// Try to get stats from the drop-in object |
| 55 |
if (is_object($wp_object_cache)) { |
| 56 |
// Some drop-ins store connection info here |
| 57 |
if ($type === 'redis') { |
| 58 |
if (method_exists($wp_object_cache, 'redis_status')) { |
| 59 |
// Try to infer from redis Object Cache dropin |
| 60 |
if (isset($wp_object_cache->redis_client)) { |
| 61 |
try { |
| 62 |
$info = $wp_object_cache->redis_client->info(); |
| 63 |
if ($info) { |
| 64 |
$status['memory_used'] = $info['used_memory'] ?? 0; |
| 65 |
$status['memory_total'] = $info['maxmemory'] ?? 0; |
| 66 |
$status['evictions'] = $info['evicted_keys'] ?? 0; |
| 67 |
if (isset($info['db0'])) { |
| 68 |
preg_match('/keys=(\d+)/', $info['db0'], $matches); |
| 69 |
$status['keys'] = isset($matches[1]) ? (int)$matches[1] : 0; |
| 70 |
} |
| 71 |
$status['hits'] = $info['keyspace_hits'] ?? 0; |
| 72 |
$status['misses'] = $info['keyspace_misses'] ?? 0; |
| 73 |
} |
| 74 |
} catch (Exception $e) {} |
| 75 |
} |
| 76 |
} |
| 77 |
|
| 78 |
// Connection type |
| 79 |
if (defined('WP_REDIS_SCHEME') && WP_REDIS_SCHEME === 'unix') { |
| 80 |
$status['connection'] = 'UNIX Socket (Fast)'; |
| 81 |
} elseif (defined('WP_REDIS_HOST')) { |
| 82 |
$status['connection'] = 'TCP ' . WP_REDIS_HOST . ':' . (defined('WP_REDIS_PORT') ? WP_REDIS_PORT : '6379'); |
| 83 |
} |
| 84 |
} elseif ($type === 'memcached') { |
| 85 |
if (method_exists($wp_object_cache, 'get_stats')) { |
| 86 |
$stats = $wp_object_cache->get_stats(); |
| 87 |
if (!empty($stats) && is_array($stats)) { |
| 88 |
$first = reset($stats); // usually keyed by server |
| 89 |
if (is_array($first)) { |
| 90 |
$status['hits'] = $first['get_hits'] ?? 0; |
| 91 |
$status['misses'] = $first['get_misses'] ?? 0; |
| 92 |
$status['memory_used'] = $first['bytes'] ?? 0; |
| 93 |
$status['memory_total'] = $first['limit_maxbytes'] ?? 0; |
| 94 |
$status['evictions'] = $first['evictions'] ?? 0; |
| 95 |
$status['keys'] = $first['curr_items'] ?? 0; |
| 96 |
} |
| 97 |
} |
| 98 |
} |
| 99 |
$status['connection'] = 'TCP (Standard)'; |
| 100 |
} |
| 101 |
|
| 102 |
// General WP Cache stats fallback |
| 103 |
if ($status['hits'] === 0 && isset($wp_object_cache->cache_hits)) { |
| 104 |
$status['hits'] = $wp_object_cache->cache_hits; |
| 105 |
$status['misses'] = $wp_object_cache->cache_misses; |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
if (($status['hits'] + $status['misses']) > 0) { |
| 110 |
$status['hit_rate'] = round($status['hits'] / ($status['hits'] + $status['misses']) * 100, 2); |
| 111 |
} |
| 112 |
|
| 113 |
if ($status['memory_total'] > 0) { |
| 114 |
$status['memory_pct'] = round($status['memory_used'] / $status['memory_total'] * 100, 1); |
| 115 |
} |
| 116 |
|
| 117 |
return $status; |
| 118 |
} |
| 119 |
|
| 120 |
public static function flush(): bool { |
| 121 |
if (!self::_pro()) return false; |
| 122 |
if (function_exists('wp_cache_flush')) { |
| 123 |
return wp_cache_flush(); |
| 124 |
} |
| 125 |
return false; |
| 126 |
} |
| 127 |
|
| 128 |
public static function format_bytes(int $bytes): string { |
| 129 |
if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB'; |
| 130 |
if ($bytes >= 1024) return round($bytes / 1024, 1) . ' KB'; |
| 131 |
return $bytes . ' B'; |
| 132 |
} |
| 133 |
|
| 134 |
public static function hit_rate_class(float $rate): string { |
| 135 |
if ($rate >= 90) return 'grade-a'; |
| 136 |
if ($rate >= 70) return 'grade-b'; |
| 137 |
if ($rate >= 50) return 'grade-c'; |
| 138 |
return 'grade-f'; |
| 139 |
} |
| 140 |
} |
| 141 |
|