Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Генератор utf-8 json на php с помощью unicode 6

Anna | 31.05.2014 | нет комментариев

Разумеется, в PHP есть очаровательная функция json_encode. Но до версии 5.3 включительно те же русские символы кодируются в виде uXXXX — в разы длиннее, чем utf-8. Дабы уменьшить объем трафика, нужно убрать реформирование utf-8 символов в u-последовательности. Да, в PHP 5.4 у json_encode наконец-то возник параметр JSON_UNESCAPED_UNICODE, но многие хостеры до сих пор представляют пользователям выбор только между версиями 5.2 и 5.3.

Я бы не стал изобретать следующий велосипед, но те решения, которые мне попадались, имеют всеобщую задачу — они правильно обрабатывают только символы базовой плоскости юникода.

Метод, в различных модификациях обширно общеизвестный на просторах интернета, заключается в том, что итог работы ф-ции json_encode обрабатывается фильтром, заменяющим все вступления uXXXX на utf-8 символы. Скажем, так:

class Json{
  static function json_encode($data){
    return preg_replace_callback('/\\u([0-9a-f]{4})/i',
      function($val){
        return mb_decode_numericentity(''.intval($val[1], 16).';', array(0, 0xffff, 0, 0xffff), 'utf-8');
      }, json_encode($data)
    );
  }
}

И данный код работал… До тех пор, пока не потребовалось добавить поддержку юникодных emoji (эмотиконы были добавлены в эталоне Unicode 6), множество из которых имеет коды больше 0x1F000 (первая плоскость unicode).

Дело в том, что u-последовательности имеют кодировку utf-16: слово (2 байта) на символ с кодом от 0×0000 до 0xFFFF (исключая «окно» 0xD800-0xDFFF) и 2 слова (4 байта) с кодами 0xD800-0xDFFF для символов с кодами больше 0xFFFF.

Скажем, начальный юникод-символ с кодом 0x1f601, имеющий utf-8 представление “xf0x9fx98x81″, будет преобразован функцией json_dencode в строку “ud83dude01″ и итогом вышеприведенной ф-ции будет строка “xedxa0xbdxedxb8x81″. Взамен одного 4-х байтового символа получили два 3-х байтовых.

Таким образом, для типичной обработки символов нужен обзор кодов и отдельное реформирование 2-х символьных u-последовательностей. Скажем, так:

class Json{
  static public $_code;

  static public function json_encode($data){
    Json::$_code=0;
    return preg_replace_callback('/\\u([0-9a-f]{4})/i',
      function($val){
        $val=hexdec($val[1]);
          if(Json::$_code){
            $val=((Json::$_code&0x3FF)<<10) ($val&0x3FF) 0x10000;
            Json::$_code=0;
          }elseif($val>=0xD800&&$val<0xE000){
            Json::$_code=$val;
            return '';
          }
          return html_entity_decode(sprintf('%x;', $val), ENT_NOQUOTES, 'utf-8');
      }, json_encode($data)
    );
  }
}

Данный вариант правильно преобразовывает всякие utf-8 символы.

P.S. Я восхитительно понимаю, что вышеприведенный код далек от оптимального. Но он работает и с довольной — для моих задач — эффективностью. А сопоставлять скорость работы всех придуманных вариантов легко лень. Вот, скажем, вариант, перекладывающий обзор на регулярное выражение:

class Json{
  static public function json_encode($data){
    return preg_replace_callback('/\\ud([89ab][0-9a-f]{2})\\ud([c-f][0-9a-f]{2})|\\u([0-9a-f]{4})/i', function($val){
      return html_entity_decode(
        empty($val[3])?
          sprintf('%x;', ((hexdec($val[1])&0x3FF)<<10) (hexdec($val[2])&0x3FF) 0x10000):
          ''.$val[3].';',
        ENT_NOQUOTES, 'utf-8'
      );
    }, json_encode($data));
  }
}

P.P.S. Вызовы html_entity_decode вставлены в callback-функцию потому, что обрабатываемые данные могут содержать html-код, включающий служебные html-сущности (‘<’, ‘>’, ‘&’ и т.д.), которые не обязаны быть преобразованы в символы.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB