Объясните, как WordPress работает с набором символов MySQL и правилами сортировки на низком уровне
Как следует из заголовка вопроса, я хочу понять, как WordPress работает с наборами символов MySQL и параметрами сортировки (collation). Как я покажу ниже, некоторые вещи кажутся мне нелогичными...
Я установил WordPress, следуя инструкциям на их странице установки:
https://codex.wordpress.org/Installing_WordPress
Следуя инструкциям, я вручную создал базу данных MySQL через командную строку, используя следующие команды:
mysql> CREATE DATABASE databasename;
Query OK, 1 row affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON databasename.* TO "wordpressusername"@"hostname"
-> IDENTIFIED BY "password";
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
mysql> EXIT
Далее, как и было указано, я отредактировал файл "wp-config.php", чтобы использовать кодировку UTF-8:
define( 'DB_CHARSET', 'utf8' );
...и оставил параметр сортировки пустым:
define( 'DB_COLLATE', '' );
И тут начинается самое интересное...
Если я ввожу в запись символ, который не входит в UTF-8 MySQL, но есть в UTF-8 MB4 (например, ), он корректно отображается на странице. Я ожидал, что этого не произойдет, так как я не устанавливал кодировку UTF-8 MB4, а выбрал более ограниченный UTF-8 (по определению MySQL, а не в общем смысле).
Если я исследую этот вопрос в MySQL через командную строку, ситуация становится еще страннее. При выполнении
show variables like 'char%';
я получаю такой результат:+--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+
Я ожидал, что кодировка базы данных будет UTF-8, а не latin1.
При выполнении команды
show variables like 'collation%';
вывод следующий:+----------------------+-------------------+ | Variable_name | Value | +----------------------+-------------------+ | collation_connection | utf8_general_ci | | collation_database | latin1_swedish_ci | | collation_server | latin1_swedish_ci | +----------------------+-------------------+
Это еще более странно по очевидным причинам (не ожидал увидеть collation latin1_swedish_ci в UTF-8 базе данных).
- Наконец, если я выполню
show full columns from mywpdatabase.wp_posts;
, в строках, где значение не NULL, отображается collation:
| post_content_filtered | longtext | utf8mb4_unicode_ci |
Мой вопрос: как это можно объяснить? Почему мой WordPress корректно отображает символы UTF-8 MB4, хотя база данных в конфигурации определена как UTF-8? И почему MySQL показывает базу данных как latin1 с collation swedish, а не UTF-8? И как получилось, что несмотря на это, отдельные поля в таблице имеют utf8mb4_unicode_ci? Очень помогло бы низкоуровневое объяснение работы WordPress с MySQL. Спасибо!

В файле wp-config.php сайта на WordPress есть две директивы:
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
Есть несколько моментов, которые чаще всего понимают неправильно. Названия констант в этих директивах могут наводить на мысль, что они относятся к самой базе данных. Но это не так. Они относятся к таблицам внутри базы данных.
Создание базы данных полностью независимо от создания таблиц. WordPress не создает базу данных и не заботится о кодировке и сортировке по умолчанию для базы данных, пока может подключиться к ней.
Значение 'utf8' в первой директиве означает наименее ограниченную кодировку из семейства 'utf8', которая может быть либо 'utf8', либо 'utf8mb4'.
Если оставить указанные директивы без изменений, перед попыткой установки сайта, это означает, что WordPress сам сделает выбор относительно кодировки и сортировки таблиц базы данных, которые поддерживаются MySQL (в зависимости от версии MySQL) и являются наименее ограничивающими.
WordPress анализирует следующие моменты для определения выбора во время установки:
- версия MySQL
- сортировка базы данных (в wp-config.php)
На основе версии MySQL WordPress решает, какую группу семейства utf8 использовать. Есть две группы, различающиеся по названию: utf8 и utf8mb4. Кодировки из группы utf8 позволяют хранение символов длиной максимум 3 байта. Кодировки из группы utf8mb4 позволяют хранение символов длиной максимум 4 байта.
Затем WordPress проверяет значение директивы DB_COLLATE. Если оно пустое, будет использована наименее ограничивающая сортировка из выбранного семейства utf8, в противном случае будет использовано указанное значение.
Примеры
define('DB_CHARSET', 'utf8'); define('DB_COLLATE', '');
Если MySQL не поддерживает utf8mb4 (старые версии), то кодировка таблиц будет utf8, а сортировка - utf8_general_ci. В противном случае можно ожидать utf8mb4 и utf8mb4_unicode_520_ci, или utf8mb4_unicode_ci (в зависимости от версии MySQL).
define('DB_CHARSET', 'utf8'); define('DB_COLLATE', 'utf8_polish_ci');
Старая версия MySQL - utf8 и utf8_polish_ci. Новая версия MySQL - utf8mb4 и utf8mb4_polish_ci (суффикс _polish_ci учитывается).
define('DB_CHARSET', 'cp1250'); define('DB_COLLATE', 'cp1250_polish_ci');
Любая версия MySQL - cp1250 и cp1250_polish_ci.
define('DB_CHARSET', 'cp1250'); define('DB_COLLATE', 'utf8_general_ci');
Любая версия MySQL - ошибка (несоответствие кодировки и сортировки).
Итог
В большинстве случаев оставлять значения указанных директив без изменений - хороший выбор. Но если вы хотите, чтобы сортировка таблиц соответствовала языку вашего сайта, можно изменить значение директивы DB_COLLATE соответствующим образом (например - utf8mb4_polish_ci).
Примечание: это объясняет, почему символ сохранялся и извлекался правильно. Просто кодировка ваших таблиц принадлежала группе utf8mb4, а не utf8.

Спасибо за объяснение того, как WordPress устанавливает кодировку, но вы не затронули остальные моменты. Почему, если определен набор символов UTF-8, MySQL показывает, что база данных использует latin1? И почему кодировка базы данных указана как swedish? Кроме того, вы, кажется, путаете набор символов и кодировку. Кодировка определяет только правила сортировки и сравнения, а не набор символов. Следовательно, независимо от используемой кодировки, если UTF-8 — это набор символов, символы за его пределами (в более узком понимании MySQL) не должны отображаться.

Я обновлю свой ответ, чтобы более четко объяснить процесс.

Спасибо за обновление! Я принял ваш ответ, теперь все понятно. Проблема в MySQL и моей недостаточной экспертизе в нем — я не знал, что таблицы могут использовать более широкий набор символов, чем сама база данных. Эта новая информация успокоила меня. Мне не нужно менять набор символов по умолчанию в MySQL, WordPress позаботится об этом на уровне таблиц.
