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

Вопрос или проблема

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

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

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

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

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

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

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

  • В вашем коде приложения (например, 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');       // объектно-ориентированный стиль
        mysqli_set_charset($link, 'utf8mb4');  // процедурный стиль
      
    • Если вы застряли на обычном mysql, но используете PHP ≥ 5.2.3, вы можете вызвать mysql_set_charset.

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

  • Та же рекомендация по utf8mb4/utf8 применима, как сказано выше.

Вывод:

  • UTF-8 должен быть установлен в HTTP-заголовке, например Content-Type: text/html; charset=utf-8. Вы можете добиться этого, установив default_charset в php.ini (предпочтительно), или вручную, используя функцию header().
  • Если ваше приложение передает текст другим системам, их также необходимо информировать о кодировке символов. С веб-приложениями браузер должен быть проинформирован о кодировке, в которой данные отправляются (через заголовки HTTP-ответа или метаданные HTML).
  • При кодировании вывода с помощью json_encode() добавьте JSON_UNESCAPED_UNICODE в качестве второго параметра.

Ввод:

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

Другие соображения по коду:

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

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

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

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

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

Не забудьте и о мета-теге (как этот, или HTML4 или XHTML версии этого):

<meta charset="utf-8">

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

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

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

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

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

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

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

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

Хорошая библиотека, на которую стоит обратить внимание, это phputf8. Она переписывает все “плохие” функции, чтобы вы могли безопасно работать с строками UTF8. Существуют также расширения, такие как mb_string, которые пытаются сделать это за вас, но я предпочитаю использовать библиотеку, потому что она более портативная (но я пишу массовые продукты, так что это важно для меня). Но phputf8 может использовать mb_string за кулисами, чтобы повысить производительность.

Предупреждение: Этот ответ относится к PHP 5.3.5 и ниже. Не используйте его для версии PHP 5.3.6 (выпущенной в марте 2011 года) или более поздних.

Сравните с ответом Palec на PDO + MySQL и сломанная кодировка UTF-8.


Я обнаружил проблему с использованием PDO, и решение заключалось в использовании этого для строки подключения PDO:

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

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

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

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

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

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

INTL

Хорошо:

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

Плохо:

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

mb_string

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

ICONV

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

БАЗЫ ДАННЫХ

  • MySQL: Символьный набор и колляция на таблицах и на соединении (не колляция). Также, не используйте mysql – mysqli или PDO
  • postgresql: pg_set_client_encoding
  • sqlite(3): Убедитесь, что она была скомпилирована с поддержкой Unicode и intl

Некоторые другие нюансы

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

Единственное, что я хотел бы добавить к этим потрясающим ответам, это подчеркнуть, что нужно сохранять ваши файлы в кодировке UTF-8. Я заметил, что браузеры принимают это свойство лучше, чем просто установка UTF-8 как кодировки вашего кода. Любой приличный текстовый редактор покажет вам это. Например, Notepad++ имеет пункт меню для кодировки файлов, и он показывает вам текущую кодировку и позволяет ее изменить. Для всех моих файлов PHP я использую UTF-8 без BOM.

Некоторое время назад кто-то попросил меня добавить поддержку UTF-8 для PHP и MySQL приложения, разработанного кем-то другим. Я заметил, что все файлы были закодированы в ANSI, поэтому мне пришлось использовать iconv, чтобы конвертировать все файлы, изменить таблицы базы данных на использование символьного набора UTF-8 и колляцию utf8_general_ci, добавить ‘SET NAMES utf8’ в уровень абстракции базы данных после подключения (если использовать 5.3.6 или раньше. В противном случае вам нужно использовать charset=utf8 в строке подключения) и изменить строковые функции на соответствующие функции с поддержкой многобайтовых строк PHP.

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

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

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

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

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

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

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

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

if (!$mysqli->set_charset("utf8")) {
    printf("Ошибка загрузки символьного набора utf8: %s\n", $mysqli->error);
} else {
   printf("Текущий символьный набор: %s\n", $mysqli->character_set_name());
}

Просмотреть код

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

Таким образом, вы должны либо использовать отдельную библиотеку для полноценной поддержки UTF-8, либо переписать все функции обработки строк самостоятельно.

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

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

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

Лучший ответ отличный. Вот что мне пришлось сделать на обычной Debian, PHP и MySQL настройке:

// Хранение
// Debian. По-видимому, уже UTF-8

// Извлечение
// База данных MySQL была сохранена в UTF-8,
// но, по-видимому, PHP запрашивал ISO 8859-1. Это сработало:
// ***обратите внимание на "utf8", без дефиса, это кодировка MySQL***
mysql_set_charset('utf8');

// Доставка
// Файл *php.ini* не имел символьного набора по умолчанию,
// (он был закомментирован, общий хост) и
// никакое кодирование HTTP не было указано в заголовках Apache.
// Это заставило Apache отправить заголовок UTF-8
// (и, возможно, заставило PHP действительно отправить UTF-8)
// ***обратите внимание на "utf-8", с дефисом, это кодировка php***
ini_set('default_charset','utf-8');

// Отправка
// Это работало во всех основных браузерах, как только Apache
// начал отправлять заголовок UTF-8. Я не добавлял
// атрибут accept-charset.

// Обработка
// Изменил несколько команд в PHP, таких как substr(),
// на mb_substr()

Этого было достаточно!

Ответ или решение

Полная поддержка UTF-8 в веб-приложениях: пошаговое руководство

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

Шаг 1: Настройка Apache

  1. Конфигурация файла .htaccess или httpd.conf:
    Убедитесь, что ваш файл .htaccess или основной конфигурационный файл Apache содержит следующие строки:

    AddDefaultCharset utf-8

    Также полезно убедиться, что включены следующие директивы:

    <Directory "/path/to/your/directory">
       Options Indexes FollowSymLinks
       AllowOverride All
    </Directory>
  2. Заголовки HTTP:
    Убедитесь, что вы отправляете правильные заголовки HTTP в ваших скриптах PHP для установки кодировки. Вы можете сделать это, добавив:

    header('Content-Type: text/html; charset=utf-8');
  3. META теги в HTML:
    Включите следующую строку в <head> вашего HTML-документа для обеспечения совместимости с различными браузерами:

    <meta charset="UTF-8">

Шаг 2: Настройка MySQL

  1. Создание базы данных и таблиц:
    При создании базы данных и таблиц укажите кодировку utf8mb4, которая поддерживает все символы UTF-8, включая эмодзи. Примеры команд:

    CREATE DATABASE example_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    CREATE TABLE example_table (
       id INT AUTO_INCREMENT PRIMARY KEY,
       text_column VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
    );
  2. Настройка соединения:
    В вашем PHP-коде установите соединение с базой данных с использованием utf8mb4:

    • Для PDO:

      $dbh = new PDO('mysql:host=hostname;dbname=example_db;charset=utf8mb4', 'username', 'password');
    • Для mysqli:

      $mysqli = new mysqli("hostname", "username", "password", "example_db");
      $mysqli->set_charset("utf8mb4");

Шаг 3: Настройка PHP

  1. Конфигурация php.ini:
    Убедитесь, что в файле php.ini установлено следующее:

    default_charset = "UTF-8"
  2. Использование многобайтовых функций:
    Поскольку стандартные функции PHP не поддерживают Unicode, используйте функции из расширения mbstring. Убедитесь, что вы используете их для всех операций со строками. Например:

    $length = mb_strlen($string, 'UTF-8');
    $substring = mb_substr($string, 0, 10, 'UTF-8');
  3. Проверка и конвертация входных данных:
    При получении данных от пользователя проверяйте, являются ли они действительными UTF-8 строками:

    if (!mb_check_encoding($input, 'UTF-8')) {
       // обработка ошибки
    }
  4. Работа с JSON:
    При кодировании JSON добавляйте JSON_UNESCAPED_UNICODE как второй аргумент к json_encode():

    $json = json_encode($data, JSON_UNESCAPED_UNICODE);

Заключительные рекомендации

  • Сохранение файлов в UTF-8: Убедитесь, что все ваши файлы PHP, HTML и другие скрипты сохранены в кодировке UTF-8 без BOM. Многие редакторы, такие как Visual Studio Code или Notepad++, предоставляют возможность проверить и изменить кодировку файлов.

  • Общий подход: Если во время разработки возникают проблемы, используйте подход "отладка по компонентам". Убедитесь, что каждый шаг (ввод, обработка, вывод) обрабатывается с компанией utf8mb4 и что все настройки соответствуют друг другу.

  • Тестирование: После завершения конфигурации проведите тестирование, создавая различные условия ввода и проверяя, обрабатываются ли специальные символы и эмодзи правильно.

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

Оцените материал
Добавить комментарий

Капча загружается...