2009-08-22 7 views
40

Я хотел бы убедиться, что все, что я знаю о UTF-8, является правильным. Я пытаюсь использовать UTF-8 некоторое время, но я продолжаю спотыкаться о все больше и больше ошибок и других странных вещей, из-за которых почти невозможно иметь 100% -ный сайт UTF-8. Где-то всегда есть что-то, что я, кажется, пропущу. Возможно, кто-то здесь может исправить мой список или ОК, поэтому я не пропущу ничего важного.Я правильно поддерживаю UTF-8 в своих PHP-приложениях?

Database

Каждый сайт должен хранить там данные где-то. Независимо от ваших настроек PHP вы также должны настроить БД. Если вы не можете получить доступ к файлам конфигурации, убедитесь, что «SET NAMES« utf8 »« как только вы подключаетесь. Кроме того, обязательно используйте utf8_ unicode_ ci на всех ваших таблицах. Это предполагает, что MySQL для базы данных, вам придется изменить для других.

Regex

Я делаю много регулярных выражений, которое more complex чем ваш средний поиск замены. Я должен помнить, чтобы использовать модификатор «/ u», чтобы PCRE doesn't corrupt my strings. Тем не менее, даже тогда есть still problems apparently.

Строковые функции

Все строки по умолчанию функций в (STRLEN(), StrPos() и т.д.) должны быть заменены на Multibyte String Functions, что смотреть на персонажа вместо байта.

Заголовки Вы должны убедиться, что ваш сервер возвращает правильный заголовок для браузера, чтобы знать, что набор символов вы пытаетесь использовать (так же, как вы должны сказать MySQL).

header ('Content-Type: text/html; charset = utf-8');

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

<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 

Вопросы

Мне нужно, чтобы преобразовать все, что я получаю от пользовательского агента (HTML-форм в & URI) в UTF-8 Есть ли при загрузке страницы или если я могу просто оставить строки/как они есть и все еще управляют ими через эти функции без проблем?

Если мне нужно преобразовать все в UTF-8 - то какие шаги я должен предпринять? mb_detect_encoding, кажется, построен для этого, но я все вижу, что люди жалуются, что это не всегда работает. mb_check_encoding также кажется, что проблема связана с хорошей строкой UTF-8 из искаженной.

ли PHP хранить строки в памяти по-разному в зависимости от того, какой кодировки он использует (например, тип файлов), или он по-прежнему сохраняется, как обычные жала с некоторыми из символов интерпретируются по-разному (например, & усилителя, против & в HTML). chazomaticus отвечает на этот вопрос:

В PHP (до PHP5, во всяком случае), строки являются только последовательности байтов. Существует не подразумеваемый или явный набор символов , связанный с ними; это что-то программист должен отслеживать.

Если a дает строку, отличную от UTF-8, функции mb_ *, это когда-нибудь вызовет проблему?

Если строка UTF некорректно закодирована, что-то пойдет не так (например, ошибка синтаксического анализа в регулярном выражении?) Или она просто пометит объект как плохой (html)? Есть ли вероятность, что неправильно закодированные строки приведут к возврату функции FALSE, потому что строка плохая?

Я слышал, что вы также должны указывать свои формы как UTF-8 (accept-charset = "UTF-8"), но я не уверен, в чем преимущество.?

Был ли UTF-16 написан для ограничения лимита в UTF-8? Как UTF-8 заканчивается для символов? (У2 (UTF) к?)

Функции

Вот пара пользовательских PHP функций, которые я нашел, но у меня нет никакого способа проверить, что они на самом деле работают. Возможно, у кого-то есть пример, который я могу использовать. Сначала это convertToUTF8(), а затем seem_utf8 из wordpress.

function seems_utf8($str) { 
    $length = strlen($str); 
    for ($i=0; $i < $length; $i++) { 
     $c = ord($str[$i]); 
     if ($c < 0x80) $n = 0; # 0bbbbbbb 
     elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb 
     elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb 
     elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb 
     elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb 
     elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b 
     else return false; # Does not match any model 
     for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? 
      if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) 
       return false; 
     } 
    } 
    return true; 
} 

function is_utf8($str) { 
    $c=0; $b=0; 
    $bits=0; 
    $len=strlen($str); 
    for($i=0; $i<$len; $i++){ 
     $c=ord($str[$i]); 
     if($c > 128){ 
      if(($c >= 254)) return false; 
      elseif($c >= 252) $bits=6; 
      elseif($c >= 248) $bits=5; 
      elseif($c >= 240) $bits=4; 
      elseif($c >= 224) $bits=3; 
      elseif($c >= 192) $bits=2; 
      else return false; 
      if(($i+$bits) > $len) return false; 
      while($bits > 1){ 
       $i++; 
       $b=ord($str[$i]); 
       if($b < 128 || $b > 191) return false; 
       $bits--; 
      } 
     } 
    } 
    return true; 
} 

Если кому-то интересно, я нашел большой пример страницы использовать when testing UTf-8.

+4

На самом деле, у вас есть это в обратном порядке. UTF-8 был написан для решения проблем с UTF-16. В частности, UTF-16 требует, чтобы каждый символ занимал 2 байта (16 бит), а нам, американским программистам, это не понравилось, так как это означало бы, что все наши файлы удваиваются по размеру, поэтому они создали UTF-8, который был назад совместим с ASCII, и, таким образом, все файлы с чистым ASCII будут действительны в UTF-8, тем самым избавив многих людей от необходимости конвертировать все их файлы исходного кода из ASCII в UTF-16. –

+0

Я не вижу упоминания о используемом mb_internal_encoding. Вы можете посмотреть на это и посмотреть, не имеет ли это никакого отношения к вам. –

+0

Для MySQL не вызывайте вручную 'set names', потому что он не обновляет кодировку, используемую для real_escape_string. Вместо этого используйте 'mysql_set_character_set'. См. Http://dev.mysql.com/doc/refman/5.0/ru/mysql-set-character-set.html и http://stackoverflow.com/a/1317239/632951 – Pacerier

ответ

20

ли мне нужно конвертировать все, что я получаю от пользовательского агента (HTML-форм в & URI) в UTF-8 при загрузке страницы

No. Агент пользователя должен быть представление данных в кодировке UTF -8; если вы не теряете пользу Unicode.

Способ обеспечения того, чтобы пользовательский агент представлял в формате UTF-8, должен обслуживать страницу, содержащую форму, которую он отправляет в кодировке UTF-8. Используйте заголовок Content-Type (и meta http-equiv тоже, если вы намерены сохранить форму и работать автономно).

Я слышал, что вы должны пометить вам формы как UTF-8 также (Accept-Charset = "UTF-8")

не делать. Это была хорошая идея в стандарте HTML, но IE так и не понял. Предполагалось указать исключительный список допустимых кодировок, но IE рассматривает его как список дополнительных кодировок, чтобы попытаться на основе каждого поля. Поэтому, если у вас есть страница ISO-8859-1 и форма «accept-charset =« UTF-8 », IE сначала попытается закодировать поле как ISO-8859-1, а если есть не-8859-1 персонажа там, затем он прибегнет к UTF-8.

Но поскольку IE не говорит вам, использовал ли он ISO-8859-1 или UTF-8, это абсолютно бесполезно для вас. Вы должны были бы угадать, для каждого поля отдельно, какая кодировка использовалась! Не полезно. Опустите атрибут и покажите свои страницы как UTF-8; это лучшее, что вы можете сделать в данный момент.

Если строка UTF неправильно закодирован воли что-то пойдет не так

Если вы позволите такой последовательности пройти в браузере вы можете быть в беде. Существуют «чередующиеся последовательности», которые кодируют кодовую точку с низким номером в более длинной последовательности байтов, чем это необходимо. Это означает, что если вы фильтруете «<», ища этот символ ASCII в последовательности байтов, вы можете пропустить один, и пусть элемент сценария в том, что вы считаете безопасным текстом.

Очередные последовательности были запрещены еще в ранние дни Юникода, но Microsoft потребовала очень много времени, чтобы собрать их дерьмо: IE интерпретирует байтовую последовательность «\ xC0 \ xBC» как «<» до IE6 Пакет обновления 1. Opera также ошибалась до (примерно, я думаю) версии 7. К счастью, эти старые браузеры вымирают, но по-прежнему стоит фильтровать чересстрочные последовательности в случае, если эти браузеры все еще существуют (или новые браузеры идиот делают такая же ошибка в будущем). Вы можете сделать это и исправить другие плохие последовательности, с регулярным выражением, которое позволяет использовать только правильный UTF-8, например this one от W3.

Если вы используете функции mb_ в PHP, вы можете устранить эти проблемы . Я не могу сказать точно, поскольку mb_ * был непригодным для использования, когда я все еще писал PHP.

В любом случае, это также хорошее время для удаления управляющих символов, которые являются большим и обычно недооцененным источником ошибок. Я бы удалил символы 9 и 13 из поданной строки в дополнение к остальным, которые вызывается регулярным выражением W3; также стоит удалить простые символы новой строки для строк, которые, как вы знаете, не должны быть многострочными текстовыми полями.

Был ли UTF-16 написан для ограничения лимита в UTF-8?

Нет, UTF-16 является кодирование двух байт-за элемент кода, который используется, чтобы сделать строки индексации Unicode проще в памяти (от дней, когда все Unicode будет вписываться в два байта, системы, такие как Windows, и Java все еще делает это именно так). В отличие от UTF-8, он несовместим с ASCII и практически не используется в Интернете.Но вы иногда встречаете его в сохраненных файлах, обычно сохраняемых пользователями Windows, которые были введены в заблуждение описанием Windows UTF-16LE как «Unicode» в меню Save-As.

seems_utf8

Это очень неэффективно по сравнению с регулярным выражением!

Кроме того, обязательно используйте utf8_unicode_ci для всех ваших таблиц.

Вы можете на самом деле уйти без этого, рассматривая MySQL как хранилище только для байтов и только интерпретируя их как UTF-8 в своем скрипте. Преимущество использования utf8_unicode_ci заключается в том, что он будет сопоставлять (сортировать и делать нечувствительные к регистру сравнения) знания о не-ASCII-символах, так, например. «Ŕ» и «Ŕ» - это один и тот же символ. Если вы используете сортировку без UTF8, вы должны придерживаться двоичного (с учетом регистра) соответствия.

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

+1

Спасибо за ссылку на функцию W3. Я нашел версию PHP в документах http://us3.php.net/manual/en/function.mb-detect-encoding.php#68607 – Xeoncross

+2

Вы сказали: «Не используйте accept-charset on forms», потому что это doesn корректно работать в IE для форм, отличных от UTF8.Есть ли смысл добавлять 'accept-charset =" UTF-8 "' (который я не слышал, содержит проблему), если ваша страница уже находится в UTF-8? – philfreo

+2

@philfreo: Нет, добавление 'accept-charset =" UTF-8 "' на странице, которая уже является UTF-8, не будет иметь никакого эффекта (ни в браузерах, которые следуют стандарту, ни в IE). – bobince

0

UTF-8 в порядке и не имеет ограничений, разрешенных UTF-16. PHP не меняет способ хранения строк в памяти (в отличие от Python). Если весь поток данных использует UTF-8 (веб-формы получают данные UTF-8, таблицы используют кодировку utf8, и вы используете SET NAMES utf8, и данные сохраняются без изменения (без преобразования кодировки), это должно быть хорошо.

+0

, кстати, вы должны использовать utf8_general_ci в своей БД. у вас не будет никаких проблем, которые могут возникнуть из-за использования utf8_unicode_ci –

0

для входа пользователей из формы я добавить этот атрибут в form сек теги моих: accept-charset="utf-8" Таким образом, данные, которые вы получите должны всегда быть в кодировке UTF-8 кодируются

+1

. Я боюсь, что это ненадежно, как правильно сказал bobince. Вы должны установить заголовок или метатег, чтобы заставить браузер использовать utf-8. Это автоматически заставит формы на странице отправлять данные как utf-8. –

3

базы данных/MySQL:.. Если вы используете SET NAMES и например, php/mysql, вы оставляете в поле зрения mysql_real_escape_string() изменения в кодировке символов. Это может привести к неправильным результатам. Таким образом, если вы полагаетесь на функцию escape, такую ​​как mysql_real_escape_st ring (потому что вы не используете подготовленные операторы) SET NAMES является субоптимальным решением. Вот почему mysql_set_charset() было введено или почему gentoo применяет патч, который добавляет параметр конфигурации mysql.connect_charset для php/mysql и php/mysqli.

Обычно клиент не указывает кодировку параметров, которые он отправляет. Если вы ожидаете кодированные данные utf-8 и рассматриваете его как таковые, могут быть ошибки кодирования (байтовые последовательности, которые недействительны в utf-8). Таким образом, данные могут отображаться не так, как ожидалось, или синтаксический анализатор может прервать разбор. Но, по крайней мере, пользовательский ввод не может «убежать» и принести больше вреда, например. в встроенной инструкции sql или выходе html. Например.взять скрипт (сохраненный в ISO-8859-1 или UTF-8, не имеет значения)

<?php 
$s = 'abcxyz'; 
var_dump(htmlspecialchars($s, ENT_QUOTES, 'utf-8')); 
// adding the byte sequence for äöü in iso-8859-1 
$s = 'abc'. chr(0xE4) . chr(0xF6) . chr(0xFC). 'xyz'; 
var_dump(htmlspecialchars($s, ENT_QUOTES, 'utf-8')); 

печатает

string(6) "abcxyz" 
string(0) "" 

E4F6FC не является допустимым UTF-8 последовательность байт, поэтому htmlspecialchars возвращается пустую строку. Другие функции могут вернуться? или другой «особый» характер. Но, по крайней мере, они не будут «ошибочно» воспринимать персонажа как злонамеренного контрольного персонажа - если все они придерживаются «правильной» кодировки (в данном случае - utf-8).

accept-charset не гарантирует, что вы получите только данные с этой кодировкой. Насколько вам известно, клиент может даже не «использовать»/анализировать ваш html-документ, содержащий элемент формы. Это может помочь, и нет причин, по которым вы не должны устанавливать этот атрибут. Но это не «надежный».

+0

Что касается SET NAMES: В основном, до PHP 5.2.3, mysql_real_escape_string было бесполезно, если вы не могли изменить конфигурацию сервера и не соответствовали тому, что вам нужно? Это действительно похоже на то, что должно быть явно написано в документах PHP - и это также звучит так, как будто мне нужно обновить свой код БД, чтобы быть в безопасности ... –

+3

Хотя http://php.net/mysql_set_charset не объясняет, почему SET NAMES может быть плохим, по крайней мере, он говорит: «Использование mysql_query() для выполнения SET NAMES .. не рекомендуется». – VolkerK

+0

Причина, по которой не использовать SET NAMES в запросе, заключается в том, что более старые и даже «современные» функции MySQLi и PDO, например, для экранирования (mysqli_real_escape_string()/PDO :: quote()) не учитывают набор символов, который был по запросу. Вам нужно использовать [mysqli] -> set_charset()/"charset = utf8" в строке подключения PDO. –

11

Большая часть того, что вы делаете сейчас, должна быть правильной.

Некоторые примечания: любая сортировка в MySQL будет хранить ваши данные правильно как UTF-8, единственная разница между ними - это сортировка (в алфавитном порядке), применяемая при сортировке.

Вы можете сообщить Apache и PHP, чтобы задать правильные заголовки набора символов AddDefaultCharset utf-8 в httpd.conf/.htaccess и default_charset = "utf-8" в php.ini соответственно.

Вы можете указать расширение mbstring, чтобы заботиться о строковых функциях. Это работает для меня:

mbstring.internal_encoding=utf-8 
mbstring.http_output=UTF-8 
mbstring.encoding_translation=On 
mbstring.func_overload=6 

(это оставляет mail() функция нетронутым - я обнаружил, установив его на 7 разрушила мои почтовые заголовки)

Для преобразования кодировки посмотрите на https://sourceforge.net/projects/phputf8/.

PHP не заботится о том, что находится в переменной, он просто хранит и получает слепо его содержимое.

У вас появятся неожиданные результаты, если вы объявите один номер mbstring.internal_encoding и передадите строки функций mb_ * в другую кодировку. В любом случае вы можете безопасно отправлять ASCII в функции utf-8.

Если вы беспокоитесь о том, что кто-то разместил неправильно закодированные материалы, я считаю, что вы должны рассмотреть HTML Purifie r для фильтрации данных GET/POST перед обработкой.

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

UTF-16 не является крупным братом UTF-8, он просто служит другой цели.