Сегодня поговорим о том, как определить IP адрес пользователя в PHP, как его проверить с помощью регулярных выражений, как его конвертировать в число и обратно, как его хранить в базе данных MySQL, а так же о том, какие ip адреса заранее зарезервированы..
Вступление
IP адрес - это сокращение от Internet Protocol Address. Это числовой идентификатор компьютера или устройства в локальной сети или сети интернет. Бывают двух версий:
- IPv4 (32 бита), пример: 192.168.0.1
- IPv6 (128 бит), пример: 0000:0000:0000:0000:0000:0000:0000:1 или ::1
В этой статье мы поговорим про ip-адреса версии IPv4, т.е. об ip адресах из диапазона: 0.0.0.0 - 255.255.255.255
Как получить IP адрес посетителя в PHP
IP адрес пользователя, в PHP, передается в глобальном массиве $_SERVER. Получить его можно использовав ключ массива REMOTE_ADDR, пример:
<?php $ip = $_SERVER['REMOTE_ADDR']; echo $ip;
В результате выполнения скрипта, вы получите ip адрес посетителя.
Какой адрес будет показан при входе с моего ПК, на веб-сервер на моем ПК?
В случае, если Вы установили веб-сервер на свой ПК (например, Openserver) и открываете страницу опять же со своего ПК, вы увидите адрес 127.0.0.1. Это нормально, при переносе на нормальный хостинг, будет показан Ваш внешний IP.
Что будет при попытке получить ip-адрес выполнив скрипт из консоли?
Если Вы попробуете запустить такой скрипт из консоли, например создав файл ip.php с содержимым:
<?php error_reporting(-1); $ip = $_SERVER['REMOTE_ADDR']; echo $ip;
и затем в консоли выполните:
# php ip.php
то вы получите ошибку, в которой будет написано, что PHP не может найти значение по указанному ключу
Notice: Undefined index: REMOTE_ADDR in ip.php on line 3
Дело в том, что эти переменные устанавливает веб-сервер и при запуске "напрямую" они не будут установлены
Как проверить откуда запускается скрипт из консоли или через веб-сервер?
Проверить откуда запускается скрипт можно с помощью функции php_sapi_name()
<?php $ip = null; if ((php_sapi_name() != 'cli')) { $ip = $_SERVER['REMOTE_ADDR']; }
Более надежными способом будет дополнительная проверка на существование ключа массива, вот так:
<?php $ip = null; if ((php_sapi_name() != 'cli') && isset($SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; }
Если у Вас возникнет вопрос, почему просто не проверить через isset, отвечаю: переменная REMOTE_ADDR может быть установлена с помощью переменных окружения, тогда проверка isset пройдет, но значение может не соответствовать действительности. Да, можно проигнорировать проверку с помощью php_sapi_name(), но вы должны отдавать себе отчет в том, что вы делаете и почему именно так.
Как проверить IP адрес с помощью регулярного выражения?
Проверить IP адрес с помощью регулярного выражения можно простой проверкой:
$ip = $_SERVER['REMOTE_ADDR']; if (preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$~', $ip)) { echo 'Корректный'; } else { echo 'Не корректный'; }
Здесь проверяется, что в $ip содержится такая строка:
(1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9) точка (1-3 цифры от 0 до 9)
Более точной будет проверка ip-адреса вот таким выражением:
$ip = $_SERVER['REMOTE_ADDR']; if (preg_match('~^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$~', $ip)) { echo 'Корректный'; } else { echo 'Не корректный'; }
Здесь проверяется, что в $ip содержится такая строка:
(цифра от 0 до 255) точка (цифра от 0 до 255) точка (цифра от 0 до 255) точка (цифра от 0 до 255)
Как определить IP адрес посетителя, находящегося за прокси сервером?
Достоверно на 100% определить нельзя. Можно попытать удачи с переменными HTTP_CLIENT_IP и HTTP_X_FORWARDED_FOR, но надо помнить что все переменные массива $_SERVER начинающиеся на HTTP_* это заголовки запроса, которые могут быть подделаны, переопределены, полностью отсутствовать, иметь не верный формат или содержать какую-нибудь гадость, например, sql-инъекцию.
Обычно используют так:
$ip = null; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif(!empty($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } echo $ip
В случае, если Вы решите использовать именно такой код, я рекомендую проверять его с помощью регулярки описанной выше, чтобы отфильтровать не валидные ip и гадости. Вот так:
<?php
error_reporting(-1);
$ip = null;
if (php_sapi_name() != 'cli') {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif(!empty($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
if (!preg_match('~^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$~', $ip)) {
$ip = null;
}
}
echo $ip
Как хранить IP адрес в базе данных MySQL?
Лично я предпочитаю хранить IP адрес в MySQL в поле типа VARCHAR(15), по длине самого длинного IPv4 адреса = 255.255.255.255
Другой способ, это привести IP адрес в к типу UNSIGNED INT. Это позволяет экономить память: 4 байта у UNSIGNED INT против 15 у CHAR.
Преобразовать IPv4 адрес к типу INT в PHP можно так
// ip адрес в число echo ip2long('127.0.0.1'); > 2130706433 // число в ip адрес long2ip('2130706433'); > 127.0.0.1
Тоже самое можно сделать и на стороне MySQL, для этого надо использовать функции INET_ATON() и INET_NTOA()
Запомнить эти функции достаточно просто:
ATON = Address TO Numaber
NTOA = Number TO Adress
Пример использования
# ip адрес в число SELECT INET_ATON('127.0.0.1'); > 2130706433 # число в ip-адрес SELECT INET_ATON('2130706433'); > 127.0.0.1
Выбор, что использовать для хранения VARCHAR или INT стоит за проектировщиком БД и удобством использования в будущем.
Какие IP адреса являются предопределенными?
Для локальных сетей предопределены такие диапазоны IP адресов:
- 10.0.0.0/8 или 10.0.0.0 - 10.255.255.255
- 172.16.0.0/12 или 172.16.0.0 - 172.31.255.255
- 192.168.0.0/16 или 192.168.0.0 - 192.168.255.255
Для коммуникаций внутри хоста (между программами запущенными на вашем ПК) используется диапазон ip-адресов: 127.0.0.0/8 или 127.0.0.0 - 127.255.255.255 .
Блок с 169.254.1.0 по 169.254.254.255 (подсеть 169.254.0.0/16 за исключением подсетей 169.254.0.0/24 и 169.254.255.0/24) —
используется для автоматической настройки сетевого интерфейса в случае отсутствия DHCP