前端分流页功能
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use GeoIp2\Database\Reader;
|
||||
use GeoIp2\Exception\AddressNotFoundException;
|
||||
|
||||
/**
|
||||
* 基于 GeoLite2-Country.mmdb 的 IP 国家查询(MaxMind GeoIP2)
|
||||
*/
|
||||
class SplitGeoIpService
|
||||
{
|
||||
/** 项目根目录下的 MaxMind 国家库文件名 */
|
||||
private const DB_FILENAME = 'GeoLite2-Country.mmdb';
|
||||
|
||||
private static ?Reader $reader = null;
|
||||
|
||||
/**
|
||||
* 解析 IP 对应 ISO 3166-1 alpha-2 国家代码;无法解析时返回 null
|
||||
*/
|
||||
public static function getCountryIso2(string $ip): ?string
|
||||
{
|
||||
$ip = trim($ip);
|
||||
if ($ip === '' || filter_var($ip, FILTER_VALIDATE_IP) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
self::bootstrapLibrary();
|
||||
|
||||
try {
|
||||
$record = self::getReader()->country($ip);
|
||||
$code = $record->country->isoCode ?? null;
|
||||
if (!is_string($code) || $code === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return strtoupper($code);
|
||||
} catch (AddressNotFoundException $e) {
|
||||
return null;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MMDB 绝对路径
|
||||
*/
|
||||
public static function getDatabasePath(): string
|
||||
{
|
||||
return rtrim((string) ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . self::DB_FILENAME;
|
||||
}
|
||||
|
||||
public static function isDatabaseAvailable(): bool
|
||||
{
|
||||
$path = self::getDatabasePath();
|
||||
|
||||
return is_file($path) && is_readable($path);
|
||||
}
|
||||
|
||||
private static function bootstrapLibrary(): void
|
||||
{
|
||||
static $bootstrapped = false;
|
||||
if ($bootstrapped) {
|
||||
return;
|
||||
}
|
||||
$bootstrapped = true;
|
||||
$loader = ROOT_PATH . 'patches/third_party/load_geoip2.php';
|
||||
if (is_file($loader)) {
|
||||
require_once $loader;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getReader(): Reader
|
||||
{
|
||||
if (self::$reader instanceof Reader) {
|
||||
return self::$reader;
|
||||
}
|
||||
if (!self::isDatabaseAvailable()) {
|
||||
throw new \RuntimeException('GeoLite2-Country.mmdb not readable at project root');
|
||||
}
|
||||
self::$reader = new Reader(self::getDatabasePath());
|
||||
|
||||
return self::$reader;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user