UTF-8 на всем протяжении


Я настраиваю новый сервер и хочу полностью поддерживать UTF-8 в своем веб-приложении. Я пробовал в прошлом на существующих серверах и, похоже, всегда заканчивал тем, что возвращался к ISO-8859-1.

Где именно мне нужно установить кодировку/кодировки? Я знаю, что для этого мне нужно настроить Apache, MySQL и PHP - есть ли какой-то стандартный контрольный список, которому я могу следовать, или, возможно, устранить неполадки, когда возникают несоответствия?

Это для нового сервера Linux под управлением MySQL 5, PHP 5 и Apache 2.

Author: Machavity, 2008-11-11

13 answers

Хранение Данных:

  • Укажите набор символов utf8mb4 для всех таблиц и текстовых столбцов в вашей базе данных. Это заставляет MySQL физически хранить и извлекать значения, закодированные изначально в UTF-8. Обратите внимание, что MySQL будет неявно использовать кодировку utf8mb4, если заданы параметры сортировки utf8mb4_* (без какого-либо явного набора символов).

  • В более старых версиях MySQL (utf8, который поддерживает только подмножество Символы Юникода. Хотел бы я, чтобы это была шутка.

Доступ к Данным:

  • В коде вашего приложения (например, PHP), в любом методе доступа к БД, который вы используете, вам нужно будет установить кодировку соединения на utf8mb4. Таким образом, MySQL не выполняет преобразование из своего собственного UTF-8, когда он передает данные вашему приложению, и наоборот.

  • Некоторые драйверы предоставляют свой собственный механизм настройки набора символов подключения, который одновременно обновляет свой собственное внутреннее состояние и информирует MySQL о кодировке, которая будет использоваться в соединении - обычно это предпочтительный подход. В PHP:

    • Если вы используете уровень абстракции PDO с PHP ≥5.3.6, вы можете указать charset в DSN:

      $dbh = new PDO('mysql:charset=utf8mb4');
      
    • Если вы используете mysqli, вы можете позвонить set_charset():

      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
      
    • Если вы застряли с простым mysql, но случайно используете PHP ≥5.2.3, вы можете позвонить mysql_set_charset.

  • Если драйвер не предоставляет свой собственный механизм для установки набора символов соединения, вам, возможно, придется отправить запрос, чтобы сообщить MySQL, как ваше приложение ожидает, что данные о соединении будут закодированы: SET NAMES 'utf8mb4'.

  • То же самое соображение относительно utf8mb4/utf8 применяется, как указано выше.

Выход:

  • Если ваше приложение передает текст в другие системы, они также должны быть проинформированы о кодировке символов. В веб-приложениях браузер должен быть проинформирован о кодировке, в которой отправляются данные (через заголовки ответов HTTP или метаданные HTML).

  • В PHP вы можете использовать default_charset опция php.ini или вручную введите заголовок Content-Type MIME самостоятельно, что просто требует больше работы, но имеет тот же эффект.

Ввод:

  • К сожалению, вы должны убедитесь, что каждая полученная строка является допустимой UTF-8, прежде чем пытаться сохранить ее или использовать где-либо. PHP - это mb_check_encoding() делает трюк, но вы должны использовать его религиозно. На самом деле это невозможно обойти, так как вредоносные клиенты могут отправлять данные в любой кодировке, которую они хотят, и я не нашел способа заставить PHP сделать это для вас надежно.

  • Из моего прочтения текущей спецификации HTML следующие подзаголовки больше не являются необходимыми или даже допустимыми для современный HTML. Я понимаю, что браузеры будут работать с данными и отправлять их в наборе символов, указанном для документа. Однако, если вы ориентируетесь на более старые версии HTML (XHTML, HTML4 и т.д.), эти пункты все еще могут быть полезны:

    • Только для HTML до HTML5: вы хотите, чтобы все данные, отправляемые вам браузерами, были в UTF-8. К сожалению, если вы воспользуетесь единственным способом надежно сделать это, добавьте атрибут accept-charset ко всем вашим тегам <form>: <form ... accept-charset="UTF-8">.
    • Только для HTML до HTML5: обратите внимание, что спецификация HTML W3C гласит, что клиенты "должны" по умолчанию отправлять формы обратно на сервер в любой кодировке, которую обслуживал сервер, но это, по-видимому, только рекомендация, следовательно, необходимо указывать каждый тег <form>.

Другие Соображения по Коду:

  • Очевидно, что все файлы, которые вы будете обслуживать (PHP, HTML, JavaScript и т.д.), Должны быть закодировано в допустимом UTF-8.

  • Вам нужно убедиться, что каждый раз, когда вы обрабатываете строку UTF-8, вы делаете это безопасно. Это, к сожалению, самая трудная часть. Вы, вероятно, захотите широко использовать PHP mbstring расширение.

  • Встроенные строковые операции PHP не по умолчанию безопасны в UTF-8. Есть некоторые вещи, которые вы можете безопасно выполнять с помощью обычных строковых операций PHP (например, конкатенация), но для большинства вещей вам следует используйте эквивалентную функцию mbstring.

  • Чтобы знать, что вы делаете (читай: не испортите это), вам действительно нужно знать UTF-8 и как он работает на минимально возможном уровне. Проверьте любую из ссылок из utf8.com для некоторых хороших ресурсов, чтобы узнать все, что вам нужно знать.

 884
Author: chazomaticus, 2017-05-23 12:34:44

Я хотел бы добавить одну вещь к превосходному ответу чазоматика:

Не забывайте также о метатеге (например, так, или его HTML4 или XHTML-версия):

<meta charset="utf-8">

Это кажется тривиальным, но IE7 уже доставлял мне проблемы с этим раньше.

Я все делал правильно; база данных, подключение к базе данных и HTTP-заголовок типа содержимого были установлены в UTF-8, и он отлично работал во всех других браузерах, но Internet Explorer все еще настаивал на использовании Кодировка "западноевропейская".

Оказалось, что на странице отсутствовал МЕТА-тег. Добавление этого решило проблему.

Редактировать:

На самом деле в W3C есть довольно большой раздел , посвященный I18N. У них есть ряд статей, связанных с этой проблемой, описывающих аспекты HTTP, (X)HTML и CSS:

Они рекомендуют использовать как заголовок HTTP, так и метатег HTML (или объявление XML в случае, если XHTML используется в качестве XML).

 136
Author: mercator, 2017-05-23 12:02:49

В дополнение к настройке default_charset в php.ini, вы можете отправить правильную кодировку с помощью header() из своего кода перед любым выводом:

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

Работать с Юникодом в PHP легко, если вы понимаете, что большинство строковых функций не работают с Юникодом, а некоторые могут полностью исказить строки. PHP считает, что "символы" имеют длину 1 байт. Иногда это нормально (например, explode() ищет только последовательность байтов и использует ее в качестве разделителя - так что это не имеет значения какие реальные персонажи вы ищете). Но в других случаях, когда функция на самом деле предназначена для работы с символами , PHP понятия не имеет, что ваш текст содержит многобайтовые символы, которые находятся в Юникоде.

Хорошей библиотекой для проверки является phputf8. Это перезаписывает все "плохие" функции, чтобы вы могли безопасно работать со строками UTF8. Есть расширения, такие как расширение mbstring, которые тоже пытаются сделать это для вас, но я предпочитаю использовать библиотеку, потому что это больше портативный (но я пишу продукты массового рынка, так что это важно для меня). Но phputf8 в любом случае может использовать mbstring за кулисами, чтобы повысить производительность.

 55
Author: chroder, 2008-11-10 21:35:46

Старая тема, я знаю. Обнаружена проблема с кем-то, использующим PDO, и ответом было использовать это для строки подключения PDO:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

Сайт, с которого я это взял, не работает, к счастью, его удалось получить с помощью кэша Google.

 26
Author: Brad F Jacobs, 2014-01-26 19:02:46

В моем случае я использовал mb_split, который использует регулярное выражение. Поэтому мне также пришлось вручную убедиться, что кодировка регулярного выражения была utf-8, выполнив mb_regex_encoding('UTF-8');

В качестве примечания я также обнаружил, запустив mb_internal_encoding(), что внутренняя кодировка не была utf-8, и я изменил это, запустив mb_internal_encoding("UTF-8");.

 20
Author: JDelage, 2012-02-23 22:20:22

Во-первых, если вы находитесь в

Я удивлен, что никто не упомянул о международный библиотека, которая имеет хорошую поддержку unicode, графемы, строковые операции , локализация и многое другое, см. Ниже.

Я приведу некоторую информацию о поддержке юникода в PHP от Элизабет Смит слайды на PHPBenelux'14

МЕЖДУНАРОДНЫЙ

Хорошо:

  • Оболочка вокруг библиотеки интенсивной терапии
  • Стандартизированные локали, набор локали для каждого сценария
  • Форматирование чисел
  • Форматирование валюты
  • Форматирование сообщения (заменяет gettext)
  • Календари, даты, часовой пояс и время
  • Транслитератор
  • Средство проверки подделки
  • Пакеты ресурсов
  • Преобразователи
  • Идентификационный номер поддержка
  • Графемы
  • Сопоставление
  • Итераторы

Плохо:

  • Не поддерживает zend_multibite
  • Не поддерживает преобразование ввода-вывода HTTP
  • Не поддерживает перегрузку функций

Mb_строка

  • Включает поддержку zend_multibyte
  • Поддерживает прозрачную кодировку HTTP in/out
  • Предоставляет некоторые обертки для функциональности, такие как стратоуппер

ЗНАЧОК V

  • Основной для преобразования кодировки
  • Обработчик выходного буфера
  • функциональность кодирования mime
  • преобразование
  • некоторые помощники строк (len, substr, strpos, strrpos)
  • Фильтр потока stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

БАЗЫ ДАННЫХ

  • mysql: Набор символов и параметры сортировки в таблицах и при подключении (не параметры сортировки). Также не используйте mysql - msqli или PDO
  • postgresql (почтовый сервер): кодирование pg_set_client_encoding
  • sqlite(3): Убедитесь, что он был скомпилирован с поддержкой unicode и intl

Некоторые другие готы

  • Вы не можете использовать имена файлов в юникоде с PHP и Windows, если вы не используете расширение 3-й части.
  • Отправляйте все в ASCII, если вы используете exec, proc_open и другие вызовы командной строки
  • Обычный текст не является обычным текстом, файлы имеют кодировки
  • Вы можете конвертировать файлы на лету с помощью iconv фильтр

Я обновлю этот ответ на случай, если что-то изменится, добавленные функции и так далее.

 19
Author: Jimmy Kane, 2014-02-16 17:37:22

Недавно я обнаружил, что использование strtolower() может вызвать проблемы, когда данные усекаются после специального символа.

Решение состояло в том, чтобы использовать

mb_strtolower($string, 'UTF-8');

Mb_ использует многобайтовый. Он поддерживает больше символов, но в целом работает немного медленнее.

 13
Author: Notflip, 2016-05-04 12:26:56

Единственное, что я хотел бы добавить к этим удивительным ответам, - это подчеркнуть необходимость сохранения ваших файлов в кодировке utf8, я заметил, что браузеры принимают это свойство вместо установки utf8 в качестве вашей кодировки кода. Любой приличный текстовый редактор покажет вам это, например, в Notepad++ есть пункт меню для ввода файла, он показывает текущую кодировку и позволяет ее изменить. Для всех моих php-файлов я использую utf8 без спецификации.

Некоторое время назад кто-то попросил меня добавить поддержку utf8 для приложение php/mysql, разработанное кем-то другим, я заметил, что все файлы были закодированы в ANSI, поэтому мне пришлось использовать ICONV для преобразования всех файлов, изменить таблицы базы данных для использования кодировки utf8 и сортировки utf8_general_ci, добавить "НАБОР ИМЕН utf8" на уровень абстракции базы данных после подключения (если используется 5.3.6 или более ранняя версия, в противном случае вы должны использовать кодировку=utf8 в строке подключения) и изменить строковые функции для использования эквивалента многобайтовых строковых функций php.

 12
Author: Puerto AGP, 2015-06-17 00:20:58

В PHP вам нужно будет либо использовать многобайтовые функции , либо включить mbstring.func_overload. Таким образом, такие вещи, как strlen, будут работать, если у вас есть символы, которые занимают более одного байта.

Вам также потребуется определить набор символов ваших ответов. Вы можете либо использовать AddDefaultCharset, как указано выше, либо написать PHP-код, который возвращает заголовок. (Или вы можете добавить МЕТА-тег в свои HTML-документы.)

 8
Author: JW., 2008-11-10 21:29:21

Я только что прошел через ту же проблему и нашел хорошее решение в руководствах по PHP.

Я изменил всю свою кодировку файлов на UTF8, а затем кодировку по умолчанию в моем соединении. Это решило все проблемы.

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

Просмотр источника

 8
Author: Abdul Sadik Yalcin, 2017-01-21 14:24:09

Поддержка Юникода в PHP по-прежнему представляет собой огромный беспорядок. Хотя он способен преобразовывать строку ISO8859 (которую он использует внутренне) в utf8, ему не хватает возможности работать со строками юникода изначально, что означает, что все функции обработки строк будут искажать и повреждать ваши строки. Поэтому вам придется либо использовать отдельную библиотеку для надлежащей поддержки utf8, либо переписать все функции обработки строк самостоятельно.

Самая простая часть - это просто указать кодировку в заголовках HTTP и в база данных и тому подобное, но все это не имеет значения, если ваш PHP-код не выводит допустимый UTF8. Это самая трудная часть, и PHP практически не помогает вам в этом. (Я думаю, что PHP6 должен исправить худшее из этого, но до этого еще далеко)

 6
Author: jalf, 2014-02-11 19:49:45

Лучший ответ - отличный. Вот что мне пришлось сделать при обычной настройке debian/php/mysql:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

Вот и все!

 5
Author: commonpike, 2011-01-14 16:13:18

Если вы хотите, чтобы сервер MySQL определял набор символов, а не PHP в качестве клиента (старое поведение; предпочтительнее, на мой взгляд), попробуйте добавить skip-character-set-client-handshake в свой my.cnf в разделе [mysqld] и перезапустите mysql.

Это может вызвать проблемы, если вы используете что-либо, кроме UTF8.

 5
Author: Nikola Tulimirovic, 2015-02-11 23:52:55