1

X-Forwarded-For и Opera

Браузеры фирмы Opera (десктопный в режиме Turbo, мобильные Mini и Mobile) используют прокси-сервера компании в Норвегии и других странах мира для сжатия трафика. Это очень удобно при плохой связи с Интернетом, особенно на мобильных телефонах. Но создает проблемы при попытке узнать IP-адрес подключившегося пользователя, например, для геолокации. Благо создатели браузера спешат нам на помощь. Специально для таких случаев они посылают нестандартизированный HTTP-загловок X-Forwarded-For, впервые внедренный для прокси-сервера Squid и впоследствии ставший стандартом де-факто для прокси. Формат заголовка:

X-Forwarded-For: {IP-адрес} [ , {IP-адрес} ]*

То есть X-Forwarded-For представляет собой строку IP-адресов, разделенных запятой, где адреса идут в порядке прокси-серверов от пользователя к серверам компании Opera (ну или любого прокси, поддерживающего этот заголовок).

Для определения географического положения пользователя нужно брать последний адрес из перечисленных в заголовке X-Forwarded-For — адрес, с которого пользователь пришел на сервера Opera. Первый может вполне быть внутренним прокси-сервером домашней сети и пользы от него нам никакой. Последний адрес может быть адресом другого прокси, но тут уж ничего не поделаешь.

Т.е. чтобы определить IP пользователя:

  1. смотрим заголовок X-Forwarded-For,
  2. если есть берем последний адрес в списке,
  3. если нет, то берем IP-адрес подключения.

Или как-то так на PHP:


function getUserIp() {
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $forwarded = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim(array_pop($forwarded));
    }
    return $_SERVER['REMOTE_ADDR'];
}

И да, полностью полагаться на геолокацию на основе IP неправильно и всегда нужно давать пользователю возможность свое местоположение уточнить.

Кстати, X-Forwarded-For не единственный заголовок, выставляемый прокси. Интернеты сообщают еще о существовании заголовков X-REAL-IP, VIA и других. Но Opera их не использует :)

7

Кодировки при работе с DOMDocument

DomDocument — мощный инструмент работы с XML и HTML с использованием методов DOM. Главное при его использовании не запутаться в кодировках.

Конструктор DOMDocument принимает два параметра — версию и кодировку документа

$dom = new DOMDocument([ string $version [, string $encoding ]]);

А чтобы начать работать с существующим HTML-документом его надо загрузить в объект DOMDocument:

$dom->loadHTML( string $source );

Проблема в том, что документ загружается совсем не в той кодировке, что указана в конструкторе. Вообще, параметры конструктора никак не влияют на загруженный документ. А используются только при методах save():

$htmlString = $dom->saveHTML ();

Так что параметры конструктора можно совсем опустить, если вы собираетесь только работать с существующим деревом и не планируете его изменять и выгружать.

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


$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $source);

Либо сконвертировать все символы документа в HTML-entities перед импортом


$source = mb_convert_encoding($source, 'HTML-ENTITIES', 'utf-8');
$dom->loadHTML($source);

Первый вариант кажется предпочтительнее в плане производительности. В плане результатов работы различий замечено не было.

Если работать с DOM планируется много, то удобно оформить эти конструкции в отдельный класс наследника DOMDocument, добавить в методе загрузки дерева второй параметр кодировки и определить метод, например, так:


class MyDOMDocument extends DOMDocument {
    public function loadHTML($source, $encoding) {
        $source = '<meta http-equiv="Content-Type" content="text/html; charset='.
            $encoding.'">' . $source
        $dom->loadHTML('' . $source);
    }
}
Copyright © 2017 — dec5e | Site design by Trevor Fitzgerald