UTF-8 полностью через

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

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

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

Ответ 1

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

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

  • В более старых версиях MySQL (<5.5.3) вам, к сожалению, придется использовать просто utf8, который поддерживает только подмножество символов Unicode. Я хотел бы шутить.

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

  • В коде вашего приложения (например, PHP), в каком бы методе доступа к БД вы ни использовали, вам нужно установить для charset соединения значение 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 относится к utf8mb4/utf8 как и выше.

Выход:

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

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

  • При кодировании вывода с использованием json_encode() добавьте JSON_UNESCAPED_UNICODE в качестве второго параметра.

Вход:

  • К сожалению, вы должны проверить каждую полученную строку как действительную UTF-8, прежде чем пытаться сохранить ее или использовать где-либо. PHP mb_check_encoding() делает 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, вы делаете это безопасно. Это, к сожалению, самая сложная часть. Возможно, вы захотите широко использовать mbstring PHP mbstring.

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

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

Ответ 2

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

Не забудьте также тег META (например, или версию HTML4 или XHTML):

<meta charset="utf-8">

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

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

Оказалось, что на странице отсутствует тег META. Добавление этого решения проблемы.

Edit:

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

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

Ответ 3

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

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

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

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

Ответ 4

Я обнаружил проблему с кем-то, использующим 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.

Ответ 5

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

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

Ответ 6

Прежде всего, если вы находитесь в < 5.3PHP, то нет. У тебя много проблем, чтобы справиться.

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

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

INTL

Хорошо:

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

Плохо:

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

mb_string

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

Iconv

  • Первичный для преобразования кодировки
  • Обработчик выходного буфера
  • функция кодирования 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

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

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

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

Ответ 7

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

Решение заключалось в использовании

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

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

Я только что прошел ту же проблему и нашел хорошее решение в руководствах 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());
}

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

Ответ 11

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

Предполагается, что мистический PHP6 должен все это выпрямиться, верно?

Вы можете в значительной степени настроить utf-8 как глобальную кодировку по умолчанию для mysql на уровне сервера, и она по умолчанию будет правильно соответствовать более гранулированным уровням.

Ответ 12

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

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

Ответ 13

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

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

Ответ 14

Верхний ответ отличный. Вот что я должен был сделать на обычной настройке 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

Это все!

Ответ 15

Если вы хотите решение MySQL, у меня были аналогичные проблемы с 2 из моих проектов, после миграции сервера. После поиска и опробования множества решений я наткнулся на это/ничего до того, как это сработало):

mysqli_set_charset($con,"utf8");

После добавления этой строки в мой конфигурационный файл все работает отлично!

Я нашел это решение https://www.w3schools.com/PHP/func_mysqli_set_charset.asp, когда искал решение для вставки из HTML-запроса

удачи!

Ответ 16

Просто примечание:

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

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

Применение того, что вы узнали из ответов на этот вопрос на свежие данные, может решить вашу проблему.

Ответ 18

Размещено как сообщество wiki:

Для пользователей WordPress:

Sidenote: вопрос был отменен. Сообщение было взято из:

Частичное сообщение:

У меня есть сайт wordpress, который я установил на своем локальном хосте. Я только что загрузил его на мой хостинг и импортировал базу данных, но все они отображаются как.


Решение из OP:

Для всех, у кого есть эта проблема, ниже это исправлено для меня. Он не был связан с базой данных.

Требуется обновление wp-config.php. Я изменил define('DB_CHARSET', 'utf8mb4'); на define('DB_CHARSET', 'utf8');