Explicación detallada de cómo WordPress trabaja con el conjunto de caracteres y la intercalación de MySQL a bajo nivel
Como sugiere el título de la pregunta, busco entender cómo WordPress trabaja con los conjuntos de caracteres y las opciones de intercalación de MySQL. Como mostraré a continuación, algunas cosas no tienen mucho sentido para mí...
Instalé WordPress siguiendo las instrucciones de su página de instalación:
https://codex.wordpress.org/Installing_WordPress
Como parte de las instrucciones, seguí su consejo para la creación manual de la base de datos MySQL en la línea de comandos, específicamente estos comandos:
mysql> CREATE DATABASE nombredelbasededatos;
Query OK, 1 row affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON nombredelbasededatos.* TO "nombredeusuariowp"@"nombredehost"
-> IDENTIFIED BY "contraseña";
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
mysql> EXIT
Además, como se indicó, edité el archivo "wp-config.php" para usar el conjunto de caracteres UTF-8:
define( 'DB_CHARSET', 'utf8' );
...y dejé la configuración de intercalación vacía:
define( 'DB_COLLATE', '' );
Aquí es donde comienza lo interesante...
Si ingreso un carácter que no es parte del UTF-8 de MySQL, pero sí de UTF-8 MB4, como , en una publicación, aparece correctamente en la página renderizada. Hubiera esperado que esto no ocurriera, ya que no he configurado el conjunto de caracteres como UTF-8 MB4, sino el más restrictivo UTF-8 (según lo define MySQL, no como se entiende generalmente).
Si investigo el tema en MySQL desde la línea de comandos, se vuelve más extraño. Si ejecuto
show variables like 'char%';
, obtengo esta respuesta:+--------------------------+----------------------------+ | 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/ | +--------------------------+----------------------------+
Hubiera esperado que el conjunto de caracteres de la base de datos fuera UTF-8, no latin1.
Si ejecuto el comando
show variables like 'collation%';
, la salida es:+----------------------+-------------------+ | Variable_name | Value | +----------------------+-------------------+ | collation_connection | utf8_general_ci | | collation_database | latin1_swedish_ci | | collation_server | latin1_swedish_ci | +----------------------+-------------------+
Eso es aún más extraño, por razones obvias (no hubiera esperado la intercalación predeterminada latin1_swedish_ci en una base de datos UTF-8).
- Finalmente, si ejecuto
show full columns from miwpdatabase.wp_posts;
, las líneas de salida, donde el valor no es NULL, muestran que la intercalación es:
| post_content_filtered | longtext | utf8mb4_unicode_ci |
Mi pregunta entonces - ¿cómo se puede explicar esto? ¿Por qué mi instalación de WordPress muestra correctamente caracteres UTF-8 MB4, cuando la base de datos está definida como UTF-8 en la configuración? ¿Y por qué MySQL muestra la base de datos como latin1 con intercalación sueca, en lugar de UTF-8? ¿Y cómo es que, a pesar de todo esto, los campos individuales en la tabla son utf8mb4_unicode_ci? Una explicación a bajo nivel de cómo WordPress trabaja con MySQL sería muy útil. ¡Gracias!

Hay dos definiciones en el archivo wp-config.php de un sitio WordPress:
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
Hay varias cosas que comúnmente se malinterpretan. Los nombres de las constantes en estas definiciones podrían sugerir que están relacionadas con la base de datos en sí. No es así. Están relacionadas con las tablas dentro de la base de datos.
La creación de la base de datos es totalmente independiente de la creación de tablas. WordPress no crea una base de datos y no se preocupa por el conjunto de caracteres y la intercalación predeterminados de la base de datos, siempre y cuando pueda conectarse a ella.
El valor 'utf8' en la primera definición significa el conjunto de caracteres menos restrictivo de la familia 'utf8', que es 'utf8' o 'utf8mb4'.
Si dejas las definiciones anteriores sin cambios, antes de intentar instalar tu sitio, es como decirle a WordPress que tome sus propias decisiones con respecto al conjunto de caracteres y la intercalación de las tablas de la base de datos, que son compatibles con MySQL (dependiendo de la versión de MySQL) y son los menos limitantes.
A continuación, se detallan los aspectos que WordPress analiza para determinar sus elecciones durante la instalación:
- Versión de MySQL
- Intercalación de la base de datos (en wp-config.php)
Según la versión de MySQL, WordPress decide qué grupo de la familia utf8 usar. Hay dos grupos, distinguidos por sus nombres: utf8 y utf8mb4. Los conjuntos de caracteres del grupo utf8 permiten almacenar caracteres de un máximo de 3 bytes de longitud. Los conjuntos de caracteres del grupo utf8mb4 permiten almacenar caracteres de un máximo de 4 bytes de longitud.
Ahora, WordPress verifica el valor de la definición DB_COLLATE. Si está vacío, utilizará la intercalación menos limitante de la familia utf8 elegida; de lo contrario, usará el valor especificado.
Ejemplos
define('DB_CHARSET', 'utf8'); define('DB_COLLATE', '');
Si MySQL no soporta utf8mb4 (versiones antiguas), entonces el conjunto de caracteres de las tablas será utf8 y la intercalación será utf8_general_ci. De lo contrario, podemos esperar utf8mb4 y utf8mb4_unicode_520_ci, o utf8mb4_unicode_ci (dependiendo de la versión de MySQL), respectivamente.
define('DB_CHARSET', 'utf8'); define('DB_COLLATE', 'utf8_polish_ci');
Versión antigua de MySQL - utf8 y utf8_polish_ci. Versión nueva de MySQL - utf8mb4 y utf8mb4_polish_ci (se respeta el sufijo _polish_ci)
define('DB_CHARSET', 'cp1250'); define('DB_COLLATE', 'cp1250_polish_ci');
Cualquier versión de MySQL - cp1250 y cp1250_polish_ci.
define('DB_CHARSET', 'cp1250'); define('DB_COLLATE', 'utf8_general_ci');
Cualquier versión de MySQL - error (incompatibilidad entre el conjunto de caracteres y la intercalación)
Resumen
En la mayoría de los casos, dejar los valores de las definiciones explicadas anteriormente sin cambios es una buena opción. Pero, si deseas que la intercalación de las tablas coincida con el idioma de tu sitio, puedes modificar el valor de la definición DB_COLLATE apropiadamente (por ejemplo - utf8mb4_spanish_ci).
Nota: Esto explica por qué el carácter se almacenó y recuperó correctamente. Simplemente, el conjunto de caracteres de tus tablas pertenecía al grupo utf8mb4, no a utf8.

Gracias por explicar cómo WordPress establece la intercalación, pero no has abordado los demás puntos. ¿Por qué, si está definido el juego de caracteres UTF-8, MySQL muestra la base de datos como latin1? ¿Y por qué muestra la intercalación de la base de datos como sueco? Además, parece que estás confundiendo el juego de caracteres y la intercalación. La intercalación solo define las reglas de ordenación y comparación, no el juego de caracteres. Por lo tanto, no importa qué intercalación se use, si UTF-8 es el juego de caracteres, los caracteres fuera de este (como se define en el sentido más estrecho de MySQL) no deberían renderizarse.

Actualizaré mi respuesta para explicar más claramente el proceso.

¡Gracias por la actualización! He aceptado tu respuesta, ahora todo está claro. El problema está con MySQL y mi falta de experiencia en él; no sabía que las tablas pueden usar un juego de caracteres más amplio que la base de datos en sí. Esta nueva información me ha tranquilizado. No necesito cambiar el juego de caracteres predeterminado en MySQL, WordPress se encarga de ello a nivel de tabla.
