Files
CLOAK/lib/Cloak/ClientIpResolver.php
T
2026-06-15 22:42:59 +08:00

90 lines
2.3 KiB
PHP
Executable File

<?php
/**
* CDN / 反代场景下解析访客真实 IP
*/
class ClientIpResolver
{
/**
* @return string
*/
public static function resolve()
{
static $resolved = null;
if ($resolved !== null) {
return $resolved;
}
$candidates = [];
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$candidates[] = (string) $_SERVER['HTTP_CF_CONNECTING_IP'];
}
if (!empty($_SERVER['HTTP_TRUE_CLIENT_IP'])) {
$candidates[] = (string) $_SERVER['HTTP_TRUE_CLIENT_IP'];
}
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$candidates[] = (string) $_SERVER['HTTP_X_REAL_IP'];
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
foreach (explode(',', (string) $_SERVER['HTTP_X_FORWARDED_FOR']) as $part) {
$part = trim($part);
if ($part !== '' && strcasecmp($part, 'unknown') !== 0) {
$candidates[] = $part;
}
}
}
if (!empty($_SERVER['REMOTE_ADDR'])) {
$candidates[] = (string) $_SERVER['REMOTE_ADDR'];
}
foreach ($candidates as $candidate) {
$ip = self::normalizeIp($candidate);
if ($ip === null) {
continue;
}
if (self::isPublicIp($ip)) {
$resolved = $ip;
return $resolved;
}
}
foreach ($candidates as $candidate) {
$ip = self::normalizeIp($candidate);
if ($ip !== null) {
$resolved = $ip;
return $resolved;
}
}
$resolved = '0.0.0.0';
return $resolved;
}
/**
* @param string $ip
*/
public static function normalizeIp($ip)
{
$ip = trim($ip);
if ($ip === '' || strcasecmp($ip, 'unknown') === 0) {
return null;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) === false) {
return null;
}
return $ip;
}
/**
* @param string $ip
*/
private static function isPublicIp($ip)
{
return filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) !== false;
}
}