Получение IP посетителя и данных геолокации с помощью PHP

Потребность узнать с помощью PHP IP посетителя, а также его геолокацию на практике возникает нередко. Например, нам необходимо иметь возможность запрещать показ контента для пользователей из одного региона и разрешать для посетителей из другого. В этой статье мы рассмотрим, как узнать IP посетителя в PHP, и определим страну, код страны и город, используя API стороннего сервиса геолокации.

Как узнать IP посетителя в PHP

Информация об IP пользователя, который заходит на наш сайт, находится в суперглобальном массиве $_SERVER. Если посетитель не использует прокси-сервер, его IP можно узнать, получив значение элемента массива $_SERVER по ключу REMOTE_ADDR:

// В переменную $ip будет присвоен ip-адрес посетителя
$ip = $_SERVER['REMOTE_ADDR'];

В случае когда пользователь использует прокси, найти его реальный IP будет сложнее, так как $_SERVER['REMOTE_ADDR'] будет содержать адрес не посетителя, а прокси-сервера. В такой ситуации нужно проверить значения по двум другим ключам суперглобального массива — $_SERVER['HTTP_CLIENT_IP'] и $_SERVER['HTTP_X_FORWARDED_FOR']. В них содержатся значения заголовков, которые устанавливаются прокси-сервером и могут иметь реальный IP пользователя.

Создадим функцию, которая определяет клиентский IP посетителя, использующего прокси-сервер. Если IP будет найден, вернем его, иначе возвратим null:

function getUserIp() {
    $userIp = null;

    if (isset($_SERVER['HTTP_CLIENT_IP'])) {
        $userIp = $_SERVER['HTTP_CLIENT_IP'];
    }

    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        // клиентский IP будет первым в списке, созданным прокси-сервером
        $userIp = $ips[0];
    }

    if (isset($_SERVER['REMOTE_ADDR'])) {
        $userIp = $_SERVER['REMOTE_ADDR'];
    }
    
    if (filter_var($userIp, FILTER_VALIDATE_IP)) {
        return $userIp;
    }

    return null;
}

// Полученный ip будет находиться в переменной $userIp
$userIp = getUserIp();

Стоит быть осторожным при работе с заголовками, устанавливаемых прокси-сервером. Это может быть небезопасно по причине того, что эти заголовки можно подделать.

Данные геолокации пользователя

Зная IP, можно получить различные данные геолокации пользователя: страну, город, географические координаты и так далее. Это можно сделать с помощью внешних сервисов, позволяющих получить такую информацию по их API.

Напишем небольшой класс для определения страны, кода страны и города пользователя, просматривающего сайт. А также разберем варианты использования этого класса.

Сперва получим IP текущего посетителя, затем, используя сервис ipstack, извлечем искомую информацию. Скрипт будет работать с IP-адресом, расположенном в $_SERVER['REMOTE_ADDR'], то есть если пользователь просматривает статью, используя прокси, то сценарий будет использовать IP прокси-сервера.

Для работы с API сервиса нам нужен API-ключ. Чтобы его получить, перейдем на сайт ipstack.com и зарегистрируемся там:

Регистрация на ipstack.com

Для демонстрации возможностей сервиса нам достаточно бесплатного тарифа. Нажмем на «GET FREE API KEY»:

Тарифы на ipstack.com

Заполним регистрационную форму:

После успешной регистрации в личном кабинете найдем API-ключ и скопируем его. В последующем мы его будем использовать в нашей программе для отправки запросов на API сервиса:

API access key в ipstack.com

Далее напишем класс PHP, который будет отвечать за получение данных геолокации с ipstack. В статический метод getGeoData первым параметром будем передавать скопированный нами API-ключ. В качестве второго параметра будет IP-адрес — откроем возможность методу работать с любым IP, а не только с IP текущего пользователя. Первый параметр — обязательный. Если не передавать второй, скрипт возьмет IP из $_SERVER['REMOTE_ADDR']:

class DemoGeoDataRetriever {
    public static function getGeoData(string $apiKey, string $ip = null)
    {
        // Если не передан API key
        // или IP не валидный (если он был передан),
        // не будем делать запрос, вернув null
        if (
            empty($apiKey)
            || ($ip && !filter_var($ip, FILTER_VALIDATE_IP))
        ) {
            return null;
        }

        // Если IP передан в параметре, будем работать с ним,
        // иначе возьмем IP текущего пользователя
        $ip = $ip ?? $_SERVER['REMOTE_ADDR'];

        // Подготовим URL для запроса, вставив в него IP и API-ключ
        $apiUrl = "http://api.ipstack.com/$ip?access_key=$apiKey";

        // Получим JSON-данные
        $geoData = file_get_contents($apiUrl);
        // Сконвертируем данные в ассоциативный массив
        $result = json_decode($geoData, true);

        // Если запрос не будет удачным, API вернет массив с ключом success, который будет равен false
        // Когда запрос неуспешен, возращаем null
        if (isset($result['success']) && !$result['success']) {
            return null;
        }

        // Если все хорошо, вернем результат
        return $result;
    }
}

// Вызовем метод у класса, передав API-ключ, и результат сохраним в переменную
$demoGeoDataResult = DemoGeoDataRetriever::getGeoData('ваш_api_ключ');

Теперь у нас есть массив с данными геолокации, и мы можем использовать его по своему усмотрению. Например, можно проверить, из какой страны зашел к нам пользователь, и разделить логику для посетителей из разных стран:

if (!empty($demoGeoDataResult) && isset($demoGeoDataResult['country_code'])) {
    if ($demoGeoDataResult['country_code'] === 'US') {
        // код для посетителей из США
    } else {
        // код для пользователей остальных стран
    }
}

Либо выбрать нужные нам данные из массива и сгенерировать с ними HTML, используя, к примеру, шаблонизатор Twig.

Шаблон Twig, который будем использовать в примере, будет находиться в файле geo_data.twig:

<div class='demoGeoLocation'>
    <ul>
        {% for key, option in geo_data %}
            <li><span>{{ key }}:</span> {{ option }}</li>
        {% endfor %}
    </ul>
</div>
// Настройка Twig:
// указание папки для размещения его шаблонов ("templates")
$loader = new \Twig\Loader\FilesystemLoader('templates');
// создание объекта для рендеринга шаблонов
$view = new \Twig\Environment($loader);

// Создадим массив данных, которые хотим распечатать,
// присвоив названия, под которыми они будут отображаться
$labels = [
    'ip' => 'IP',
    'country_name' => 'Название страны',
    'country_code' => 'Код страны',
    'city' => 'Город'
];

// Получим результат от API рассматриваемого сервиса
$demoGeoDataResult = DemoGeoDataRetriever::getGeoData('ваш_api_ключ');

// Если массив с результатом пришел...
if (!empty($demoGeoDataResult)) {
    // ...выберем из него только те данные, которые есть в массиве $labels...
    $dataToPrint = array_intersect_key($demoGeoDataResult, $labels);
    // ... и с помощью возможностей Twig сгенерируем HTML, передав новый массив с четырьмя элементами файлу geo_data.twig
    $twigHtml = $view->render('geo_data.twig', ['geo_data' => $dataToPrint]);
    // Переменная $twigHtml будет содержать результирующий HTML, который можно распечатать
    echo $twigHtml;
}

Результат последнего примера для IP-адреса 8.8.8.8 (Google) будет выглядеть следующим образом:

IP посетителя и геоданные в PHP

В этой статье был рассмотрен один сервис геолокации, но их существует множество. Подробно изучайте функциональные возможности и тарифные условия каждого, чтобы выбрать подходящий, если у вас будет необходимость работать с данными геолокации.

Оцените статью
DevReflex
Добавить комментарий