TLDR
// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');
Esta respuesta tiene un énfasis en la biblioteca de PDO de PHP porque es tan ubicua.
Un breve recordatorio: mysql es una arquitectura cliente-servidor. Esto es significativo porque no solo existe el servidor mysql donde está la base de datos real, sino también el controlador de cliente mysql separado, que es lo que habla con el servidor mysql (son entidades separadas). Podría decirse que el cliente mysql y el pdo están mezclados.
Cuando usa set names utf8
, emite una consulta SQL estándar a mysql.Mientras que la consulta sql pasa a través de pdo, y luego a través de la biblioteca del cliente mysql, y finalmente llega al servidor mysql, SOLAMENTE el servidor mysql analiza e interpreta esa consulta sql. Esto es significativo porque el servidor mysql no envía ningún mensaje a pdo o al cliente mysql, haciéndole saber que el conjunto de caracteres y la codificación han cambiado, por lo que el pdo ignora por completo el hecho de que sucedió.
Es importante no hacer esto porque la biblioteca del cliente no puede manejar cadenas adecuadamente si no conoce el conjunto de caracteres actual. La mayoría de las operaciones comunes funcionarán correctamente sin que el cliente conozca el juego de caracteres correcto, pero el que no lo hará es el de escape de cadenas, como PDO::quote. Puede pensar que no necesita preocuparse por el escape de cadenas primitivas manuales porque usa sentencias preparadas, pero la verdad es que la gran mayoría de los usuarios de pdo: mysql usan sin saberlo emulated prepared statements porque ha sido la configuración predeterminada para el controlador pdo: mysql para un tiempo muy largo ahora. Una declaración preparada emulada no utiliza declaraciones preparadas de mysql nativas reales provistas por la API de mysql; en su lugar, php hace el equivalente de llamar al PDO::quote()
en todos sus valores, y str_replacing'ing todos sus marcadores de posición con los valores cotizados para usted.
Dado que no puede escapar correctamente de una cadena a menos que conozca el juego de caracteres que está utilizando, estas instrucciones preparadas emuladas son vulnerables a la inyección sql si ha cambiado a ciertos conjuntos de caracteres a través de nombres de conjuntos. Independientemente de la posibilidad de inyección sql, aún puede romper sus cadenas si utiliza un esquema de escape destinado a un conjunto de caracteres diferente.
Para el controlador pdo mysql, puede especificar el juego de caracteres cuando se conecta, por specifying it in the DSN. Tanto la biblioteca del cliente como el servidor conocerán el juego de caracteres si lo hace.
// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');
Pero el escape de cadenas incorrecto no es el único problema. Por ejemplo, también puede tener problemas con el uso de PDO::bindColumn porque los nombres de las columnas se especifican como cadenas, por lo que también importa la codificación. Un ejemplo podría ser un nombre de columna denominado ütube
(observe la diéresis), y cambie de latin
a utf8
a través de nombres de conjuntos, y luego intente con $stmt->bindColumn('ütube', $var);
con ütube
siendo una cadena codificada utf8 porque su archivo php está codificado para utf8. No funcionará, necesitarás codificar la cadena como una variante de latin1 ... y ahora tienes todo tipo de locuras sucediendo.
¿Qué técnica terminaste implementando? –