Заплатка Zend_Http_Client для поддержки кириллических доменов(ZF 1.x)
Здравствуй, уважаемый хабрачитатель. Хочу расказать о своей попытке подружить Zend Framework версии 1.8.2 с кириллическими доменами.
Работать с idn доменами Zend вроде бы умеет, однако при попытке открыть что-нибудь вроде xn--e1afmkfd.su (пример.su)
или xn--e1afmkfd.xn--p1ai (пример.рф) ругается
Fatal error:
Uncaught exception 'Zend_Uri_Exception' with message 'Invalid URI supplied' in /.../Zend/Uri/Http.php:156
Проблема замечена в версии 1.8.2, но сохраняется и до 1.11
Давайте совершим небольшой экскурс по просторам кода и посмотрим почему это происходит.
Генерирует exception конструктор класса Zend_Uri_Http, именно в нем хранит uri Zend_Http_Client.
// Validate the URI
if ($this->valid() === false) {
require_once 'Zend/Uri/Exception.php';
throw new Zend_Uri_Exception('Invalid URI supplied');
}
Как видно из кода, полученный конструктором uri проходит валидацию.
Посмотрим метод valid:
public function valid()
{
// Return true if and only if all parts of the URI have passed validation
return $this->validateUsername()
and $this->validatePassword()
and $this->validateHost()
and $this->validatePort()
and $this->validatePath()
and $this->validateQuery()
and $this->validateFragment();
}
Как видим валидаторов немало. Отладка показала, что false из-за кириллицы возвращает метод validateHost.
Клацаем на ссылочку в IDE и во внутренностях метода находим:
// Check the host against the allowed values; delegated to Zend_Filter.
$validate = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL);
Клацаем еще раз и смотрим код самого валидатора.
Код валидатора да вольно объемен и интересен. В нем хост проверяется на корректность написания по нескольким параметрам.
В частности валидатор знает много разных зон в том числе idn и проверяет написание в соответствии с правилами этих зон.
Зоны рф в списке не было, что поначалу и показалось причиной ошибки, однако это не так: валидатор легко «глотает» и неизвестные зоны,
просто для их проверки нет специальных правил.
Отладка показала, что ошибка происходит при проверке возможности декодировать домен методом decodePunycode
…
foreach ($domainParts as $domainPart) {
// Decode Punycode domainnames to IDN
if (strpos($domainPart, 'xn--') === 0) {
$domainPart = $this->decodePunycode(substr($domainPart, 4));
if ($domainPart === false) {
return false;
}
}
…
Вот мы и нашли корень зла — он в методе decodePunycode.
Метод это выглядит очень странно… Что он делает мне досконально понять не удалось очень смущает например вот такой фрагмент:
protected function decodePunycode($encoded)
{
…
$separator = strrpos($encoded, '-');
if ($separator > 0) {
for ($x = 0; $x < $separator; ++$x) {
// prepare decoding matrix
$decoded[] = ord($encoded[$x]);
}
} else {
$this->_error(self::CANNOT_DECODE_PUNYCODE);
return false;
}
…
текст метода и всего валидатора здесь
Метод выглядит неадекватным. Почему то методу «очень хочется», чтобы в строке домена (или фрагменте домена) обязательно был знак '-'.
Не разбирал досконально стандарт, поэтому не уверен, что это правило не было актуально раньше,
но сейчас такая проверка явно лишняя, т.к. в кодированных из кириллицы доменах знак '-' может не встретиться нигде, кроме префикса
(xn--e1afmkfd.su пример.su; xn--e1afmkfd.xn--p1ai пример.рф), а префикс в этот метод не попадает, он отрезается раньше, что видно из приведенного выше кода.
Может бдительный хабрачитатель просветит меня, где подправить данный метод, может быть в нем не хватает каких нибудь 2х строк для нормальной работы,
но для своей задачи мне пришлось его заменить альтернативой)
Искать альтернативу долго не пришлось: PHP богат собственными Idn Functions, которые живут требует lib idn.
У меня данная библиотека уже была установлена.
Итак меняем метод собственным.
protected function decodePunycode($encoded)
{
//HARDCODE!!!
$errorcode = 0;
$utf8 = idn_to_utf8('xn--'. $encoded, $errorcode);
if($utf8 === false) {
$this->_error(self::CANNOT_DECODE_PUNYCODE);
return false;
}
return $utf8;
}
Теперь ошибки idn кодирования отыскивает функция idn_to_utf8, и Zend_Http_Client спокойно открывает кириллические домены.
Главный изъян решения: опасность потери данного кода при обновлении версии.
С удовольствием выслушаю советы и замечания по улучшению данного кода.
Работать с idn доменами Zend вроде бы умеет, однако при попытке открыть что-нибудь вроде xn--e1afmkfd.su (пример.su)
или xn--e1afmkfd.xn--p1ai (пример.рф) ругается
Fatal error:
Uncaught exception 'Zend_Uri_Exception' with message 'Invalid URI supplied' in /.../Zend/Uri/Http.php:156
Проблема замечена в версии 1.8.2, но сохраняется и до 1.11
Давайте совершим небольшой экскурс по просторам кода и посмотрим почему это происходит.
Генерирует exception конструктор класса Zend_Uri_Http, именно в нем хранит uri Zend_Http_Client.
// Validate the URI
if ($this->valid() === false) {
require_once 'Zend/Uri/Exception.php';
throw new Zend_Uri_Exception('Invalid URI supplied');
}
Как видно из кода, полученный конструктором uri проходит валидацию.
Посмотрим метод valid:
public function valid()
{
// Return true if and only if all parts of the URI have passed validation
return $this->validateUsername()
and $this->validatePassword()
and $this->validateHost()
and $this->validatePort()
and $this->validatePath()
and $this->validateQuery()
and $this->validateFragment();
}
Как видим валидаторов немало. Отладка показала, что false из-за кириллицы возвращает метод validateHost.
Клацаем на ссылочку в IDE и во внутренностях метода находим:
// Check the host against the allowed values; delegated to Zend_Filter.
$validate = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL);
Клацаем еще раз и смотрим код самого валидатора.
Код валидатора да вольно объемен и интересен. В нем хост проверяется на корректность написания по нескольким параметрам.
В частности валидатор знает много разных зон в том числе idn и проверяет написание в соответствии с правилами этих зон.
Зоны рф в списке не было, что поначалу и показалось причиной ошибки, однако это не так: валидатор легко «глотает» и неизвестные зоны,
просто для их проверки нет специальных правил.
Отладка показала, что ошибка происходит при проверке возможности декодировать домен методом decodePunycode
…
foreach ($domainParts as $domainPart) {
// Decode Punycode domainnames to IDN
if (strpos($domainPart, 'xn--') === 0) {
$domainPart = $this->decodePunycode(substr($domainPart, 4));
if ($domainPart === false) {
return false;
}
}
…
Вот мы и нашли корень зла — он в методе decodePunycode.
Метод это выглядит очень странно… Что он делает мне досконально понять не удалось очень смущает например вот такой фрагмент:
protected function decodePunycode($encoded)
{
…
$separator = strrpos($encoded, '-');
if ($separator > 0) {
for ($x = 0; $x < $separator; ++$x) {
// prepare decoding matrix
$decoded[] = ord($encoded[$x]);
}
} else {
$this->_error(self::CANNOT_DECODE_PUNYCODE);
return false;
}
…
текст метода и всего валидатора здесь
Метод выглядит неадекватным. Почему то методу «очень хочется», чтобы в строке домена (или фрагменте домена) обязательно был знак '-'.
Не разбирал досконально стандарт, поэтому не уверен, что это правило не было актуально раньше,
но сейчас такая проверка явно лишняя, т.к. в кодированных из кириллицы доменах знак '-' может не встретиться нигде, кроме префикса
(xn--e1afmkfd.su пример.su; xn--e1afmkfd.xn--p1ai пример.рф), а префикс в этот метод не попадает, он отрезается раньше, что видно из приведенного выше кода.
Может бдительный хабрачитатель просветит меня, где подправить данный метод, может быть в нем не хватает каких нибудь 2х строк для нормальной работы,
но для своей задачи мне пришлось его заменить альтернативой)
Искать альтернативу долго не пришлось: PHP богат собственными Idn Functions, которые живут требует lib idn.
У меня данная библиотека уже была установлена.
Итак меняем метод собственным.
protected function decodePunycode($encoded)
{
//HARDCODE!!!
$errorcode = 0;
$utf8 = idn_to_utf8('xn--'. $encoded, $errorcode);
if($utf8 === false) {
$this->_error(self::CANNOT_DECODE_PUNYCODE);
return false;
}
return $utf8;
}
Теперь ошибки idn кодирования отыскивает функция idn_to_utf8, и Zend_Http_Client спокойно открывает кириллические домены.
Главный изъян решения: опасность потери данного кода при обновлении версии.
С удовольствием выслушаю советы и замечания по улучшению данного кода.
0 комментариев