4, self::VERSION_6 => 16, ]; public const DEFAULT_V4 = '127.1'; public const DEFAULT_V6 = '::1'; public static function remote(string $fallback = self::DEFAULT_V6): string { return $_SERVER['REMOTE_ADDR'] ?? $fallback; } public static function country(string $address, string $fallback = 'XX'): string { if(!GeoIP::isAvailable()) return $fallback; try { return GeoIP::resolveCountry($address)->country->isoCode ?? $fallback; } catch(AddressNotFoundException $e) { return $fallback; } } public static function rawWidth(int $version): int { return isset(self::SIZES[$version]) ? self::SIZES[$version] : 0; } public static function detectStringVersion(string $address): int { if(filter_var($address, FILTER_VALIDATE_IP) !== false) { if(filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) return self::VERSION_6; if(filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) return self::VERSION_4; } return self::VERSION_UNKNOWN; } public static function detectRawVersion(string $address): int { $addressLength = strlen($address); foreach(self::SIZES as $version => $length) { if($length === $addressLength) return $version; } return self::VERSION_UNKNOWN; } public static function cidrToRaw(string $cidr): ?array { if(strpos($cidr, '/') !== false) { [$subnet, $mask] = explode('/', $cidr, 2); } else { $subnet = $cidr; } try { $subnet = inet_pton($subnet); } catch(Exception $ex) { return null; } $mask = empty($mask) ? null : (int)$mask; return compact('subnet', 'mask'); } public static function matchCidr(string $address, string $cidr): bool { $address = inet_pton($address); $cidr = self::cidrToRaw($cidr); return self::matchCidrRaw($address, $cidr['subnet'], $cidr['mask']); } public static function matchCidrRaw(string $address, string $subnet, ?int $mask = null): bool { $version = self::detectRawVersion($subnet); if($version === self::VERSION_UNKNOWN) return false; $bits = self::SIZES[$version] * 8; if(empty($mask)) $mask = $bits; if($mask < 1 || $mask > $bits || $version !== self::detectRawVersion($subnet)) return false; for($i = 0; $i < ceil($mask / 8); $i++) { $byteMask = (0xFF00 >> max(0, min(8, $mask - ($i * 8)))) & 0xFF; $addressByte = ord($address[$i]) & $byteMask; $subnetByte = ord($subnet[$i]) & $byteMask; if($addressByte !== $subnetByte) return false; } return true; } }