get(self::REDIS_PREFIX . $ip); if ($value === false || $value === null) { return false; } return $value === self::NONE_SENTINEL ? null : (string) $value; } catch (Throwable $e) { self::logError('Redis get failed: ' . $e->getMessage()); } } return self::getFile($ip); } /** * @param string $ip * @param string|null $countryCode */ public static function set($ip, $countryCode) { $ip = ClientIpResolver::normalizeIp($ip); if ($ip === null) { return; } $value = $countryCode !== null && $countryCode !== '' ? strtoupper($countryCode) : self::NONE_SENTINEL; $ttl = self::cacheTtl(); $redis = self::redisClient(); if ($redis) { try { $redis->setex(self::REDIS_PREFIX . $ip, $ttl, $value); return; } catch (Throwable $e) { self::logError('Redis set failed: ' . $e->getMessage()); } } self::setFile($ip, $value, $ttl); } private static function cacheTtl() { $ttl = defined('CLOAK_GEOIP_CACHE_TTL') ? (int) CLOAK_GEOIP_CACHE_TTL : 2592000; return max(60, $ttl); } /** * @return Redis|null */ private static function redisClient() { if (self::$redisAvailable === false) { return null; } if (!extension_loaded('redis') || !defined('REDIS_ENABLED') || strtoupper(REDIS_ENABLED) !== 'ON') { self::$redisAvailable = false; return null; } try { $r = new Redis(); $timeout = defined('REDIS_TIMEOUT') ? (float) REDIS_TIMEOUT : 1.0; if (!@$r->connect(REDIS_HOST, (int) REDIS_PORT, $timeout)) { self::$redisAvailable = false; return null; } if (defined('REDIS_PASSWORD') && REDIS_PASSWORD !== '') { $r->auth(REDIS_PASSWORD); } self::$redisAvailable = true; return $r; } catch (Throwable $e) { self::$redisAvailable = false; return null; } } private static function cacheDir() { $dir = dirname(__DIR__, 2) . '/storage/runtime/c7f2a9e1/cc_cache'; if (!is_dir($dir)) { @mkdir($dir, 0755, true); } return $dir; } private static function cacheFilePath($ip) { return self::cacheDir() . '/' . hash('sha256', $ip) . '.json'; } /** * @param string $ip * @return string|false|null */ private static function getFile($ip) { $path = self::cacheFilePath($ip); if (!is_file($path)) { return false; } $raw = @file_get_contents($path); if ($raw === false || $raw === '') { return false; } $data = json_decode($raw, true); if (!is_array($data) || empty($data['expires_at']) || empty($data['value'])) { @unlink($path); return false; } if ((int) $data['expires_at'] < time()) { @unlink($path); return false; } return $data['value'] === self::NONE_SENTINEL ? null : (string) $data['value']; } /** * @param string $ip * @param string $value * @param int $ttl */ private static function setFile($ip, $value, $ttl) { $payload = json_encode([ 'value' => $value, 'expires_at' => time() + $ttl, ], JSON_UNESCAPED_UNICODE); if ($payload === false) { return; } @file_put_contents(self::cacheFilePath($ip), $payload, LOCK_EX); } public static function logError($message) { $handle = @fopen(dirname(__DIR__, 2) . '/err.txt', 'a+'); if ($handle) { fwrite($handle, date('Y-m-d H:i:s') . ' | GeoIpCountryCache | ' . $message . "\n"); fclose($handle); } } }