16.3.08

PHP, mySQL, UTF-8 y la madre que los...

Ustedes sabrán disculpar el exabrupto del título, pero recien acabo de terminar de solucionar un problema que, por lo que he visto en Internte mientras buscaba informanción para lograrlo, afecta a mucha gente y hay cientos, miles de soluciones, pero ninguna me funcionaba... Y la solución era bien simple.

El problema

Estoy creando un sitio basado en PHP, el servidor Apache 2.2 y como soporte de base de datos mySQL. También utilizo la tecnología AJAX, y es aquí donde comienzan mis problemas. Como ustedes quizás sepan los objetos que se utilizan para comunicarse con el servidor mediante AJAX, utilizan distintos grupos de caracteres dependiendo si la comunicación se realiza vía POST o vía GET. Y, para complicarlo aún más, si utilizas el método GET, Firefox utiliza un conjunto distinto a IE. Pero ambos coinciden en utilizar UTF-8 cuando usas el método POST. Entonces: ¡A utilizar POST! me dije.

Claro, para eso tienes que setear todo como UTF-8, inclusive tus archivos .js deben grabarse en format UTF-8 o tus alerts no se verán bien si utilizas acentos o ñ's.

Hay que tener en cuenta que el conjunto de caracteres por omisión de mySQL es LATIN1, entonces tienes que setear el archivo de inicialización (my.ini o my.cnf) para que la base trabaje con UTF-8. En el apartado [client] debes colocar:

default-character-set=utf8

y en el apartado [mysqld] debes colocar:

default-character-set=utf8
character-set-server=utf8
default-collation=utf8_unicode_ci
collation-server=utf8_unicode_ci

Además debes crear las tablas con charset UTF-8 y collation UTF8_UNICODE_CI.

Hasta aquí anda todo perfecto, según la documentación de mySQL. Pero cuando te conectas a la base de datos desde una ventana DOS y le hechas una mirada a las variables (SHOW VARIABLES), aparecen unas variables de las cuales no he encontrado documentación que indique como cambiarlas desde el archivo de inicialización y que son el meollo del asunto: character_set_client y character_set_results. La primera tiene el conjunto de caracteres que debe usar el servidor de la base de datos para traducir lo que el cliente le envía; y la otra contiene el conjunto de caracteres que debe utilizar el servidor para traducir lo que le envía al cliente.

Despues de decenas de cambios, ambas variables siempre decían LATIN1. Esto provocaba que, al enviar información a la base de datos esta se grababa reemplazando los caracteres hispánicos dos o más combinaciones de caracteres "raros".

Cuando intentaba recuperar la información desde mis páginas, esta se veía correctamente. Entonces ¿cuál era el problema? Pues bien, el problema surgiria cuando yo intentara crear una sentencia SQL válida que intentara comparar cadenas de caracteres, pues no habría forma en que coincidieran.

La solución

Pues después de mucho leer y probar, además de los parámetros de configuración que he enunciado anteriormente, luego de conectarte a la base de datos, debes ejecutar la sentencia:

SET NAMES utf8

y la felicidad volverá a tu vida.

Espero les sirva.

4 Comentarios:

matias dice...

Hola Julio!!

Si te sirve de consuelo, en PERL es también super difícil el tema de utf8!!

Un TIP, lo de "SET NAMES utf8;" esta OK, pero ojo si tu cliente se desconecta, deberás hacer el statement de vuelta. Por ahi te conviene modificar el default en my.conf en el server, si tenés acceso. Sino asegurate que este se ejecute nuevamente si el cliente se desconecta.

Saludos!
matias - Confronte.com

Julio González Seara dice...

Gracias matias por tu comentario, pero de eso se trata, cada vez que me conecto ejecuto el comando SET...
Luego de crear el objeto de la clase de manejo de la base de datos, inmediatamente ejecuto ese seteo.
Pero te cuento que existe un parámetro para my.ini que no recuenrdo bien ahora donde puedes poner la instruccion SET NAMES utf8 que, supuestametne, se ejecuta al iniciar el servidor. Pues nada... a mi no me funcionó.

CARlos NARez dice...

Me sirvió mucho tu ayuda.

Comparto mi configuración por si a alguien mas le sirve:

HTML
meta http-equiv="Content-Type" content="text/html; charset=utf-8"

PHP
@$db = mysql_connect($SERVIDOR,$USUARIO,$PASSWORD);
@mysql_select_db($BASE_DATOS,$db);
@mysql_query("SET NAMES utf8");

y en MYSQL
Cotejamiento utf8_spanish_ci

Pableco dice...

Gracias!