Разрешение неправильной кодировки символов при отображении результатов базы данных MySQL после обновления до PHP 5.3

Описание проблемы

После обновления PHP на нашем сервере разработки с 5.2 до 5.3 мы сталкиваемся с проблемой, когда данные, запрошенные из нашей базы данных и отображаемые на веб-странице, показывают неправильное кодирование при попытке отображать русские символы.

Окружающая среда

  • Dev OS: Debian GNU/Linux 6.0
  • Dev PHP: 5.3.5-0.dotdeb.1
  • Live MySQL: Распространение 5.1.49

Подробнее

В PHP 5.3 клиентская библиотека по умолчанию для взаимодействия с базами данных MySQL изменилась с libmysql на mysqlnd, что, по-видимому, было причиной проблемы, с которой мы сталкиваемся.

Мы подключаемся к базе данных со следующим кодом:

$conn = mysql_pconnect('database.hostname', 'database_user', 'database_password');
$mysql_select_db('database', $conn);

Данные, хранящиеся в нашей базе данных, кодируются кодировкой UTF-8. Подключение к базе данных через клиент командной строки и выполняемые запросы подтверждает, что данные не повреждены и правильно закодированы. Однако, когда мы запрашиваем базу данных в PHP и пытаемся отобразить одни и те же данные, она становится искаженной. В этом конкретном случае мы пытаемся отображать русские символы, а результат - неанглийские, нерусские символы: garbled mess

Полученные заголовки ответов подтверждают, что тип содержимого UTF-8:

response headers

Мы тестировали строки перед отображением mb_detect_encoding в строгом режиме, а mb_check_encoding и сказали, что строка была строкой UTF-8 перед ее отображением. Мы также использовали mysql_client_encoding для проверки клиентской кодировки, а также указывает, что набор символов - UTF-8.

При проведении исследований мы обнаружили несколько рекомендаций, чтобы попытаться обойти эту проблему:

header("Content-type: text/html; charset=utf-8");
mysql_set_charset('utf8');
mysql_query("SET SESSION character_set_results = 'UTF8'");
mysql_query('SET NAMES UTF8', $conn);

Мы даже попробовали utf8_encode:

utf8_encode($string);

Однако ни одно из этих решений не работало.

Запустив опции, мы обновили MySQL в нашей системе разработки до версии 5.1.55. После этого обновления все отображается правильно, когда мы подключались к нашей базе данных разработки. Конечно, он продолжает отображаться некорректно, когда мы подключаемся к нашей активной базе данных.

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

Ответ 1

Если вы убедились, что обе таблицы и выходная кодировка являются UTF-8, почти единственное, что осталось, это кодирование соединения.

Причиной изменения поведения при обновлении серверов может быть изменение кодировки соединения по умолчанию:

[mysql]
default-character-set=utf8

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

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

 SHOW VARIABLES LIKE 'character_set%';
 SHOW VARIABLES LIKE 'collation%'; 

Ответ 2

Я вижу, вы пробовали это, но синтаксис, который я использую: mysql_query ( "SET NAMES utf8" ). Ваш синтаксис может быть правильным, я никогда раньше этого не видел.

Пример:

// connect to database stuff
$Connection = mysql_connect($server, $username, $password)
or die ("Error connecting to server");

// connect to database stuff
$db = mysql_select_db($database, $Connection)
or die ("Error selecting database");

mysql_query("SET NAMES utf8");

Ответ 3

У меня была аналогичная проблема после обновления PHP с 5.2.3 до 5.3.5 (5.3.5-Win32-VC6-x86), MySQL 5.0.41 (не обновлено). Я думаю, что причина - небольшая разница между версиями PHP.

PHP 5.2.3 по умолчанию (без SET NAMES):
character_set_client = latin1
character_set_connection = latin1
character_set_database = utf8
character_set_filesystem = binary
character_set_results = latin1
character_set_server = latin2
character_set_system = utf8
collation_connection = latin1_swedish_ci
collation_database = utf8_polish_ci
collation_server = latin2_general_ci

PHP 5.3.5 default (без SET NAMES):
character_set_client = latin2
character_set_connection = latin2
character_set_database = utf8
character_set_filesystem = binary
character_set_results = latin2
character_set_server = latin2
character_set_system = utf8
collation_connection = latin2_general_ci
collation_database = utf8_polish_ci
collation_server = latin2_general_ci

Я добавил данные в базу данных в PHP 5.2.3 по умолчанию (без SET NAMES), поэтому теперь, чтобы правильно отобразить его, я должен прочитать его, используя:

$pdo -> query("SET NAMES 'latin1'");

Возможно, что-то подобное является причиной вашей проблемы.