Обработка сетевых адресов средствами языка PHP
Работа с IP адресами в PHP
Основное и наиболее эффективное решение
Для преобразования IPv4 адреса в целое число и обратно в PHP используются две нативные функции: ip2long и long2ip. Они работают крайне быстро и не требуют дополнительных расширений. Пример:
$ip = '192.168.1.1';
$long = ip2long($ip); // результат: -1062731519 (или 3232235777 при беззнаковом представлении)
echo $long;-1062731519
Обратное преобразование:
$long = -1062731519;
$ip = long2ip($long); // '192.168.1.1'
echo $ip;192.168.1.1
Возможная проблема:
На 32‑битных версиях PHP результат ip2long может быть отрицательным. Для получения беззнакового значения используется sprintf('%u', ip2long($ip)) или bindec(decbin(ip2long($ip))). Также ip2long не поддерживает IPv6.
Как проверить, является ли строка IP адресом?
Для проверки формата применяется filter_var() с константами FILTER_VALIDATE_IP и флагом FILTER_FLAG_IPV4 или FILTER_FLAG_IPV6.
$ip = '192.168.1.1';
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
echo "Корректный IPv4";
} else {
echo "Неверный IP";
}Типичная ошибка:
По умолчанию filter_var допускает ведущие нули (например, 192.168.001.001), что не всегда желательно. Для строгой проверки применяется регулярное выражение.
Как работать с IPv6 адресами?
Для IPv6 используются функции inet_pton (преобразование адреса в бинарную строку) и inet_ntop (обратно). Они работают как с IPv4, так и с IPv6.
$ipv6 = '::1';
$binary = inet_pton($ipv6); // 16-байтовая строка
echo bin2hex($binary); // 00000000000000000000000000000001
$back = inet_ntop($binary); // ::1
echo $back;00000000000000000000000000000001 ::1
Как преобразовать IPv4 из строки в число без потери точности?
Используется комбинация ip2long и sprintf('%u', ...). Для IPv6 удобно применять unpack после inet_pton.
$ip = '10.0.0.1';
$unsigned = sprintf('%u', ip2long($ip)); // 167772161
echo $unsigned;167772161
Как определить принадлежность IP диапазону (CIDR)?
Сначала адрес и маска преобразуются в числа, затем выполняется побитовое сравнение.
function ip_in_cidr($ip, $cidr) {
list($subnet, $bits) = explode('/', $cidr);
$ip_long = ip2long($ip);
$subnet_long = ip2long($subnet);
$mask = -1 << (32 - $bits);
return ($ip_long & $mask) === ($subnet_long & $mask);
}
var_dump(ip_in_cidr('192.168.5.10', '192.168.0.0/16')); // trueСложности:
Для IPv6 потребуется работать с 128‑битными числами, для чего применяется расширение GMP или ручной разбор бинарного представления.
Расширенные примеры работы с IP адресами
1. Извлечение IP пользователя из заголовков запроса
function getClientIP() {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ip = trim($ips[0]);
} else {
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
}
echo getClientIP();192.168.1.100 (пример)
2. Преобразование IPv6 в десятичное представление с GMP
$ipv6 = '2001:db8::1';
$binary = inet_pton($ipv6);
// Разбор 16 байт как big-endian
$hex = bin2hex($binary);
$dec = gmp_init($hex, 16);
echo gmp_strval($dec); // 4254076641128259285690398495165382656142540766411282592856903984951653826561
3. Сортировка списка IP адресов в естественном порядке
$ips = ['10.0.0.5', '192.168.0.2', '10.0.0.10', '172.16.0.1'];
usort($ips, function($a, $b) {
return ip2long($a) - ip2long($b);
});
print_r($ips);Array
(
[0] => 10.0.0.5
[1] => 10.0.0.10
[2] => 172.16.0.1
[3] => 192.168.0.2
)4. Генерация всех IP адресов подсети /24
$network = '10.0.0.0';
$mask = 24;
$long = ip2long($network);
$hosts = 1 << (32 - $mask);
for ($i = 1; $i < $hosts - 1; $i++) {
echo long2ip($long + $i) . "\n";
}10.0.0.1 10.0.0.2 ... 10.0.0.254
5. Конвертация IPv4 в IPv4‑mapped IPv6
$ipv4 = '192.168.1.1';
$ipv6 = '::ffff:' . $ipv4;
echo inet_ntop(inet_pton($ipv6)); // ::ffff:192.168.1.1::ffff:192.168.1.1
6. Определение двухбайтового представления IPv4 адреса (для базы данных)
$ip = '8.8.8.8';
$bytes = inet_pton($ip); // 4 байта
// Сохранить как BINARY(4) или CHAR(4) – хороший способ
// Чтение обратно: inet_ntop($bytes)