117 lines
3.2 KiB
PHP
117 lines
3.2 KiB
PHP
<?php
|
|
require_once __DIR__ . '/CountryCodeNormalizer.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);
|
|
$isoCode = $record->country->isoCode ?? null;
|
|
$name = $record->country->name ?? null;
|
|
|
|
$code = CountryCodeNormalizer::toAlpha2($isoCode);
|
|
if ($code === null && $name !== null && $name !== '') {
|
|
$code = CountryCodeNormalizer::toAlpha2($name);
|
|
}
|
|
return $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;
|
|
}
|
|
}
|
|
}
|