Кодирование IDN Punycode в php
Опубликовано 30 Октябрь 2013 - 11:27 пользователем bivС введением IDN вебмастера стали ускоренно знакомиться с таким понятием как punycode. Конечно же, их интересовало не столько понятие, сколько способы конвертации доменных имен в этот код и обратно.
А так как стандартом "де-факто" веб-разработки мелких и средних проектов стал интерпретируемый язык программирования php, то поиски решения задачи искали и ищут в первую очередь для него.
До данного момента сия проблема решалась двумя способами:
- Использование класса от phlylabs.
- Использование функций php -
idn_to_ascii
иidn_to_utf8
.
Все бы хорошо - конвертация стала поддерживаться языком, НО как оказалось, это возможно только с версии 5.3 и то с оговорками:
- Для версии 5.3. нужно установить расширение PECL
idn
- но для php>=5.4
idn
не соберется. Этой версии нужно расширениеintl
. - Для PECL расширений
idn
илиintl
нужна библиотека libidn - Не всякий хостинг предоставляет нужные расширения php.
- php до 5.3 не поддерживает IDN
Использование же класса - это проигрыш в скорости примерно в 20 раз.
Я написал более скоростные функции:
EncodePunycodeIDN()
- Кодирование utf-8 строки в punycode. Для её работы требуется функция ordUTF8 (функция быстрее аналогичной из класса в 2 раза)DecodePunycodeIDN()
- Декодирование punycode в строку utf-8. (функция быстрее аналогичной из класса в 1,5 раза)
Вот и сами функции:
- /**
- * Finds the character code for a UTF-8 character: like ord() but for UTF-8.
- *
- * @author Nicolas Thouvenin <nthouvenin@gmail.com>
- * @copyright 2008 Nicolas Thouvenin
- * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
- */
- function ordUTF8($c, $index = 0, &$bytes = null)
- {
- $bytes = 0;
- if ($index >= $len)
- return false;
- if ($h <= 0x7F) {
- $bytes = 1;
- return $h;
- }
- else if ($h < 0xC2)
- return false;
- else if ($h <= 0xDF && $index < $len - 1) {
- $bytes = 2;
- }
- else if ($h <= 0xEF && $index < $len - 2) {
- $bytes = 3;
- }
- else if ($h <= 0xF4 && $index < $len - 3) {
- $bytes = 4;
- }
- else
- return false;
- }
- /**
- * Encode UTF-8 domain name to IDN Punycode
- *
- * @param string $value Domain name
- * @return string Encoded Domain name
- *
- * @author Igor V Belousov <igor@belousovv.ru>
- * @copyright 2013 Igor V Belousov
- * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
- * @link http://belousovv.ru/myscript/phpIDN
- */
- function EncodePunycodeIDN($value)
- {
- /* search subdomains */
- $sub_result='';
- foreach ($sub_domain as $sub_value) {
- $sub_result .= '.'.EncodePunycodeIDN($sub_value);
- }
- }
- /* http://tools.ietf.org/html/rfc3492#section-6.3 */
- $n = 0x80;
- $delta = 0;
- $bias = 72;
- $str=$value;
- {
- }
- /* basic symbols */
- $b = $basic;
- if ($b==$input)
- {
- }
- if ($b>0) {
- $output= $basic;
- /* add delimeter */
- $output[]= '-';
- }
- /* add prefix */
- $h=$b;
- while ($h < $input_len) {
- $m=0x10FFFF;
- for ($i = 0; $i < $input_len; ++$i)
- {
- $ord_input[$i]=ordUtf8($input[$i]);
- if (($ord_input[$i] >= $n) && ($ord_input[$i] < $m))
- {
- $m = $ord_input[$i];
- }
- }
- if (($m - $n) > (0x10FFFF / ($h + 1)))
- {
- }
- $delta += ($m - $n) * ($h + 1);
- $n = $m;
- for ($i = 0; $i < $input_len; ++$i)
- {
- $c = $ord_input[$i];
- if ($c<$n)
- {
- ++$delta;
- if ($delta==0)
- {
- }
- }
- if ($c==$n)
- {
- $q = $delta;
- for ($k = 36;; $k += 36)
- {
- if ($k <= $bias)
- {
- $t = 1;
- }
- elseif ($k >= ($bias + 26))
- {
- $t = 26;
- }
- else
- {
- $t = $k - $bias;
- }
- if ($q < $t)
- {
- break;
- }
- $tmp_int=($t + (($q - $t) % (36 - $t)));
- $q = ($q - $t) / (36 - $t);
- }
- /* http://tools.ietf.org/html/rfc3492#section-6.1 */
- $delta = ($h == $b)?$delta/700:$delta>>1;
- $delta += ($delta / ($h + 1));
- $k2 = 0;
- while ($delta > 455)
- {
- $delta /= 35;
- $k2 += 36;
- }
- /* end section-6.1 */
- $delta = 0;
- ++$h;
- }
- }
- ++$delta;
- ++$n;
- }
- }
- /**
- * Decode IDN Punycode to UTF-8 domain name
- *
- * @param string $value Punycode
- * @return string Domain name in UTF-8 charset
- *
- * @author Igor V Belousov <igor@belousovv.ru>
- * @copyright 2013 Igor V Belousov
- * @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
- * @link http://belousovv.ru/myscript/phpIDN
- */
- function DecodePunycodeIDN($value)
- {
- /* search subdomains */
- $sub_result='';
- foreach ($sub_domain as $sub_value) {
- $sub_result .= '.'.DecodePunycodeIDN($sub_value);
- }
- }
- /* search prefix */
- {
- return $value;
- }
- else
- {
- $bad_input=$value;
- }
- $n = 0x80;
- $i = 0;
- $bias = 72;
- /* search delimeter */
- if ($d > 0) {
- for ( $j = 0; $j < $d; ++$j) {
- $c = $value[$j];
- $output[]=$c;
- if ($c > 0x7F)
- {
- return $bad_input;
- }
- }
- ++$d;
- } else {
- $d = 0;
- }
- {
- $oldi = $i;
- $w = 1;
- for ($k = 36;; $k += 36)
- {
- {
- return $bad_input;
- }
- $c = $value[$d++];
- $digit=($c - 48 < 10) ? $c - 22 :
- (
- ($c - 65 < 26) ? $c - 65 :
- (
- ($c - 97 < 26) ? $c - 97 : 36
- )
- );
- if ($digit > (0x10FFFF - $i) / $w)
- {
- return $bad_input;
- }
- $i += $digit * $w;
- if ($k <= $bias)
- {
- $t = 1;
- }
- elseif ($k >= $bias + 26)
- {
- $t = 26;
- }
- else
- {
- $t = $k - $bias;
- }
- if ($digit < $t) {
- break;
- }
- $w *= (36 - $t);
- }
- $delta = $i - $oldi;
- /* http://tools.ietf.org/html/rfc3492#section-6.1 */
- $delta = ($oldi == 0)?$delta/700:$delta>>1;
- $delta += ($delta / ($count_output_plus_one+1));
- $k2 = 0;
- while ($delta > 455)
- {
- $delta /= 35;
- $k2 += 36;
- }
- /* end section-6.1 */
- if ($i / $count_output_plus_one > 0x10FFFF - $n)
- {
- return $bad_input;
- }
- );
- ++$i;
- }
- }