problema de codificación entre mysql y php

Iniciado por gAb1, 27 Abril 2016, 18:11 PM

0 Miembros y 1 Visitante están viendo este tema.

gAb1

Estoy empezando a migrar (en mysql) de utf8 a utf8mb4 y a la hora de que php muestre contenido con caracteres especiales/acentuados, estos son remplazados por una interrogación con fondo negro ( � ).

Me he asegurado de que todo esté correcto. La base de datos está usando utf8mb4 (todas las tables y columnas con tipo: text, varchar, etc). Los archivos .php todos usan utf-8 (trabajo con visual studio code y esto es por defecto) y los headers, tanto en php como en html, están puestos a utf8.

Despues de instanciar mysqli, cambio el charset de utf8 a utf8mb4:

Código (php) [Seleccionar]
$mysqli->set_charset('utf8mb4');

Pero si lo dejo en utf8 todo funciona correcto... Y por lo que he estado leyendo, eso no tiene sentido alguno... Ya que en php, la codificación utf8 es completa y soporta hasta 4 bytes pero en mysql utf8 solo soporta hasta 3, por eso si el charset es utf8 pero mysql trabaja con utf8mb4 tiene que hacer conversión y es una perdida de tiempo...

¿Donde creeis que puede estar el problema?

MinusFour

¿Que caracteres son remplazados por �? Ejemplos porfavor.

gAb1

Los caracteres acentuados y demás especiales. Voy a explicarlo mejor:

Si utilizo: $mysqli->set_charset('utf8'); esto es lo que pasa:

- Los datos se guardan tal cual, por ejemplo si inserto Altaïr se guarda tal cual y cuando lo cojo con un select se muestra tal cual (el hex es 41 6C 74 61 C3AF 72).

Si utilizo $mysqli->set_charset('utf8mb4'); esto es lo que pasa:

- Los datos se guardan mal codificados, por ejemplo si inserto Altaïr esto es lo que se guarda Altaïr (el hex es 41 6C 74 61 C383C2AF 72) pero cuando hago el select es codificado y mostrado correctamente.

- Si guardo manualmente Altaïr en la columna, se muestra la interrogación con fondo negro: Alta�r (que es lo que pasa con todos los nombres de ciudades/zonas que tienen acentos).

Tengo una función de una clase que, por una razón desconocida, cuando está utf8mb4 en mysqli, los datos acentuados se muestran correctamente, pero si lo cambio a utf8, los datos que selecciona esta función son mostrados sin codificación utf8: por ejemplo, en la tabla "es" tengo Iniciar Sesión y si la codificación del mysqli es utf8 lo que se muestra es Iniciar Sesión, pero si la codificación del mysqli es utf8mb4 se muestra bien.

En resumen, si la codificación de mysqli es utf8, los datos son correctamente insertados y seleccionados en todas partes menos en la función de clase antes mencionada (que solo selecciona bien si esta utf8mb4).

Ahora bien, esta función de clase se encarga de detectar el idioma del navegador/cookie/configuración y selecciona datos de una tabla u otra, pasandole como parametro el código del texto. Esta función solo imprime bien si mysqli tiene utf8mb4....

Si necesitas ver tanto la clase loc_es o cualquier parte del código dimelo y lo puedo poner aquí si no es mucho o en un paste bin/etc.

Gracias!

MinusFour

Es un caso de doble encoding:

C3 AF los está tomando como U+00C3 y U+00AF, que son:


ï


Respectivamente.

En lugar de hacer la conversión de C3 AF a U+00EF que es:


í


Realmente no tengo una explicación para:

Alta�r

Fuera de que quizás tu navegador lo ha insertado en la tabla como:

41 6C 74 61 EF 72

Cuando el navegador encuentra EF. Lo que hace en ese momento es contar los primeros bits hasta llegar a uno que sea 0. EF:


1110 1111


Esto significa que está esperando 3 bytes para producir un símbolo y cada byte después de eso necesita empezar con: 10xxxxxx. 0x72 en binario es:


0111 0010


No puede ser un byte de continuación, por lo que la producción esta mal formada. Sin mencionar que EF señala el inicio de una secuencia de 3 bytes (cuando solo tienes 2). Ahora lo que pienso que hace el navegador es simplemente remplazar el byte que no tiene sentido por el símbolo especial de remplazo.

¿Estás seguro que la tabla tiene formato utf8? En el primer caso, se está asumiendo que la conversión es de una codificación de un solo byte (ISO-8859-1, W-1252, etc) cuando la información realmente es UTF-8. Mientras que tu segundo caso se está asumiendo que la codificación es UTF-8 pero la información realmente es una codificación de un solo byte.

gAb1

#4
Las tablas tienen todas utf8mb4 y el collation es utf8mb4_unicode_ci.

Entonces es muy raro ya que en la tabla está almacenado con ï, pero cuando hago el select hex(name) from tabla; me muestra C3 AF....

Lo de la interrogación con fondo negro solo sale cuando mysqli tiene utf8mb4 y los datos están guardados tal cual en la tabla, entonces al mostrarlos, los acentos y demás caracteres son reemplazados.

Puede ser un problema del navegador (la cache o algo)?

EDITO: Haciendo un poco de debug me he encontrado con que mysqli no acepta utf8mb4.... la versión de php es 5.4.41 y mysql 5.5.41 (utf8mb4 se introdujo en 5.5.3).

Código (php) [Seleccionar]
$mysqli = new mysqli('127.0.0.1', 'user', 'pwd', 'db_name');
$mysqli->set_charset('utf8mb4');
printf("Current character set: %s\n", mysqli_character_set_name($mysqli));


Dice "latin1"... Si quito mb4 dice "utf8"...

MinusFour

Huh, eso es interesante. Prueba con:

Código (php) [Seleccionar]

$mysqli->query("SET NAMES 'utf8mb4'");


Justo después de usar set_charset con 'utf8' (a secas).

gAb1

Dice lo mismo. ¿Crees que puede ser una configuración de php? Seguro que no es nada... sin embargo ya van dos dias perdidos buscando una solución  :-(

Gracias!

MinusFour

Lo más probable es que si sea una cosa de PHP. Prueba a hacer la query anterior y usa esta otra para verificar que valores te arroja:

Código (mysql) [Seleccionar]

SHOW VARIABLES LIKE 'character_set%';


Si puedes, prueba las consultas desde otro cliente SQL (como desde la terminal).

gAb1

Arg! Ultimamente estoy espeso.... con lo facil que habria sido comprobar el error del mysqli....

Código (php) [Seleccionar]
echo $mysqli->error;

CitarCan't initialize character set utf8mb4 (path: /usr/share/mysql/charsets/)

Código (bash) [Seleccionar]
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8mb4                    |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)


Ya le dije a mi amigo que es el que tiene acceso ssh que lo solucione.

Gracias!