300, 'detail' => 600, 'full' => 600, 'ingredients' => 600, 'ingredient_detail' => 1200, 'search' => 180, 'categories' => 1800, 'tags' => 1800, 'stats' => 120, 'stats_full' => 120, 'hot' => 300, 'query' => 180, 'filter' => 180, 'like' => 0, 'recommend' => 0, 'view' => 0 ); public static function init() { if (self::$cacheDir === null) { self::$cacheDir = dirname(__FILE__) . '/cache/'; if (!is_dir(self::$cacheDir)) { @mkdir(self::$cacheDir, 0755, true); } } } public static function getTTL($act) { return isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL; } public static function getCacheKey($act, $params = array()) { ksort($params); $paramStr = http_build_query($params); return md5($act . '_' . $paramStr); } public static function get($act, $params = array()) { self::init(); $ttl = isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL; if ($ttl <= 0) { return null; } $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; if (!file_exists($file)) { return null; } $content = file_get_contents($file); $data = json_decode($content, true); if (!$data || !isset($data['expire']) || !isset($data['data'])) { @unlink($file); return null; } if (time() > $data['expire']) { @unlink($file); return null; } return $data['data']; } public static function getStale($act, $params = array()) { self::init(); $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; if (!file_exists($file)) { return null; } $content = @file_get_contents($file); if ($content === false) { return null; } $data = json_decode($content, true); if (!$data || !isset($data['data'])) { return null; } return $data['data']; } public static function isExpired($act, $params = array()) { self::init(); $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; if (!file_exists($file)) { return true; } $content = @file_get_contents($file); if ($content === false) { return true; } $data = json_decode($content, true); if (!$data || !isset($data['expire'])) { return true; } return time() > $data['expire']; } public static function getCacheAge($act, $params = array()) { self::init(); $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; if (!file_exists($file)) { return -1; } $content = @file_get_contents($file); if ($content === false) { return -1; } $data = json_decode($content, true); if (!$data || !isset($data['created'])) { return -1; } return time() - $data['created']; } public static function set($act, $params, $data) { self::init(); $ttl = isset(self::$ttlConfig[$act]) ? self::$ttlConfig[$act] : self::$defaultTTL; if ($ttl <= 0) { return false; } $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; $cacheData = array( 'act' => $act, 'params' => $params, 'data' => $data, 'expire' => time() + $ttl, 'created' => time() ); return file_put_contents($file, json_encode($cacheData, JSON_UNESCAPED_UNICODE)) !== false; } public static function clear($act = null, $params = array()) { self::init(); if ($act === null) { $files = glob(self::$cacheDir . '*.json'); foreach ($files as $file) { @unlink($file); } return true; } $key = self::getCacheKey($act, $params); $file = self::$cacheDir . $key . '.json'; if (file_exists($file)) { @unlink($file); } return true; } public static function clearByAct($act) { self::init(); $files = glob(self::$cacheDir . '*.json'); $count = 0; foreach ($files as $file) { $content = file_get_contents($file); $data = json_decode($content, true); if ($data && isset($data['act']) && $data['act'] === $act) { @unlink($file); $count++; } } return $count; } public static function cleanExpired() { self::init(); $files = glob(self::$cacheDir . '*.json'); $count = 0; $now = time(); foreach ($files as $file) { $content = @file_get_contents($file); if ($content === false) { @unlink($file); $count++; continue; } $data = json_decode($content, true); if (!$data || !isset($data['expire']) || $now > $data['expire']) { @unlink($file); $count++; } } return $count; } public static function getStats() { self::init(); $files = glob(self::$cacheDir . '*.json'); $totalSize = 0; $count = 0; $oldest = time(); $newest = 0; foreach ($files as $file) { $count++; $totalSize += filesize($file); $content = file_get_contents($file); $data = json_decode($content, true); if ($data && isset($data['created'])) { $oldest = min($oldest, $data['created']); $newest = max($newest, $data['created']); } } return array( 'count' => $count, 'total_size' => $totalSize, 'total_size_readable' => self::formatBytes($totalSize), 'oldest' => $oldest > 0 ? date('Y-m-d H:i:s', $oldest) : '-', 'newest' => $newest > 0 ? date('Y-m-d H:i:s', $newest) : '-' ); } private static function formatBytes($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } else { return $bytes . ' bytes'; } } } if (php_sapi_name() === 'cli' && isset($argv[0]) && basename($argv[0]) === 'cache.php') { $action = $argv[1] ?? 'stats'; switch ($action) { case 'clean': $count = ApiCache::cleanExpired(); echo "Cleaned {$count} expired cache files.\n"; break; case 'clear': ApiCache::clear(); echo "All cache cleared.\n"; break; case 'stats': default: $stats = ApiCache::getStats(); echo "Cache Statistics:\n"; echo " Files: {$stats['count']}\n"; echo " Size: {$stats['total_size_readable']}\n"; echo " Oldest: {$stats['oldest']}\n"; echo " Newest: {$stats['newest']}\n"; break; } }