$ttl) { @unlink($cacheFile); return null; } $contents = @file_get_contents($cacheFile); if ($contents === false) { return null; } $data = @unserialize($contents); if ($data === false) { @unlink($cacheFile); return null; } // Store in memory cache self::$memoryCache[$key] = $data; return $data; } /** * Set cached events for a month * * @param string $namespace Namespace filter * @param int $year Year * @param int $month Month * @param array $events Events data * @return bool Success status */ public static function setMonthEvents($namespace, $year, $month, array $events) { $key = self::getMonthCacheKey($namespace, $year, $month); // Store in memory cache self::$memoryCache[$key] = $events; $cacheFile = self::getCacheFile($key); $serialized = serialize($events); // Use temp file for atomic write $tempFile = $cacheFile . '.tmp'; if (@file_put_contents($tempFile, $serialized) === false) { return false; } if (!@rename($tempFile, $cacheFile)) { @unlink($tempFile); return false; } return true; } /** * Invalidate cache for a specific month * * @param string $namespace Namespace filter * @param int $year Year * @param int $month Month */ public static function invalidateMonth($namespace, $year, $month) { $key = self::getMonthCacheKey($namespace, $year, $month); // Clear memory cache unset(self::$memoryCache[$key]); // Delete cache file $cacheFile = self::getCacheFile($key); if (file_exists($cacheFile)) { @unlink($cacheFile); } } /** * Invalidate cache for a namespace (all months) * * @param string $namespace Namespace to invalidate */ public static function invalidateNamespace($namespace) { $ns = preg_replace('/[^a-zA-Z0-9_-]/', '_', $namespace ?: 'default'); $pattern = self::getCacheDir() . "events_{$ns}_*.cache"; foreach (glob($pattern) as $file) { @unlink($file); } // Clear matching memory cache entries $prefix = "events_{$ns}_"; foreach (array_keys(self::$memoryCache) as $key) { if (strpos($key, $prefix) === 0) { unset(self::$memoryCache[$key]); } } } /** * Invalidate all event caches */ public static function invalidateAll() { $pattern = self::getCacheDir() . "events_*.cache"; foreach (glob($pattern) as $file) { @unlink($file); } // Clear memory cache self::$memoryCache = []; } /** * Get cache statistics * * @return array Cache stats */ public static function getStats() { $cacheDir = self::getCacheDir(); $files = glob($cacheDir . "*.cache"); $stats = [ 'files' => count($files), 'size' => 0, 'oldest' => null, 'newest' => null, 'memory_entries' => count(self::$memoryCache) ]; foreach ($files as $file) { $size = filesize($file); $mtime = filemtime($file); $stats['size'] += $size; if ($stats['oldest'] === null || $mtime < $stats['oldest']) { $stats['oldest'] = $mtime; } if ($stats['newest'] === null || $mtime > $stats['newest']) { $stats['newest'] = $mtime; } } return $stats; } /** * Clean up expired cache files * * @param int $ttl TTL in seconds * @return int Number of files cleaned */ public static function cleanup($ttl = self::DEFAULT_TTL) { $cacheDir = self::getCacheDir(); $files = glob($cacheDir . "*.cache"); $cleaned = 0; $now = time(); foreach ($files as $file) { $mtime = filemtime($file); if ($mtime !== false && ($now - $mtime) > $ttl) { if (@unlink($file)) { $cleaned++; } } } return $cleaned; } }