Files
CLOAK/lib/Cloak/GeoIpCountryResolver.php
T

112 lines
3.0 KiB
PHP

<?php
class GeoIpCountryResolver
{
/** @var GeoIp2\Database\Reader|null */
private static $reader = null;
/** @var array<string, string|null> */
private static $requestCache = [];
/** @var string|null */
private static $lastSource = null;
/**
* @param string $ip
* @return array{code:?string, source:string}
*/
public static function resolveWithMeta($ip)
{
$ip = ClientIpResolver::normalizeIp($ip);
if ($ip === null) {
self::$lastSource = 'invalid_ip';
return ['code' => null, 'source' => 'invalid_ip'];
}
if (array_key_exists($ip, self::$requestCache)) {
self::$lastSource = 'request';
return ['code' => self::$requestCache[$ip], 'source' => 'request'];
}
$cached = GeoIpCountryCache::get($ip);
if ($cached !== false) {
self::$requestCache[$ip] = $cached;
self::$lastSource = 'cache';
return ['code' => $cached, 'source' => 'cache'];
}
$code = self::lookupMmdb($ip);
GeoIpCountryCache::set($ip, $code);
self::$requestCache[$ip] = $code;
self::$lastSource = 'mmdb';
return ['code' => $code, 'source' => 'mmdb'];
}
/**
* @param string $ip
* @return string|null ISO 3166-1 alpha-2
*/
public static function resolve($ip)
{
$result = self::resolveWithMeta($ip);
return $result['code'];
}
public static function lastSource()
{
return self::$lastSource ?? '';
}
public static function isAvailable()
{
return self::getReader() !== null;
}
/**
* @param string $ip
* @return string|null
*/
private static function lookupMmdb($ip)
{
$reader = self::getReader();
if ($reader === null) {
return null;
}
try {
$record = $reader->country($ip);
$code = $record->country->isoCode ?? null;
if ($code === null || $code === '') {
return null;
}
return strtoupper((string) $code);
} catch (GeoIp2\Exception\AddressNotFoundException $e) {
return null;
} catch (Throwable $e) {
GeoIpCountryCache::logError('mmdb lookup failed: ' . $e->getMessage());
return null;
}
}
/**
* @return GeoIp2\Database\Reader|null
*/
private static function getReader()
{
if (self::$reader !== null) {
return self::$reader;
}
if (!class_exists('GeoIp2\\Database\\Reader')) {
return null;
}
if (!defined('CLOAK_GEOIP_DB_PATH') || !is_file(CLOAK_GEOIP_DB_PATH)) {
return null;
}
try {
self::$reader = new GeoIp2\Database\Reader(CLOAK_GEOIP_DB_PATH);
return self::$reader;
} catch (Throwable $e) {
GeoIpCountryCache::logError('Reader init failed: ' . $e->getMessage());
return null;
}
}
}