php - setnames - Si se debe usar "SET NAMES"
php mysql query set names utf8 (3)
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 pdo de php porque es muy omnipresente.
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 los 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 cliente mysql y el pdo son totalmente ignorantes del 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. Las operaciones más comunes funcionarán correctamente sin que el cliente conozca el conjunto de caracteres correcto, pero el que no lo hará es el de cadena escapándose, 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 sin saberlo utilizan sentencias preparadas emuladas porque ha sido la configuración predeterminada para el pdo: mysql conductor durante mucho tiempo 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 a PDO::quote()
en todos sus valores, y str_replacing''ing todos sus marcadores de posición con los valores cotizados para usted.
Como 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 juegos de caracteres a través de set names
. 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 conjunto de caracteres cuando se conecta, al especificarlo en el DSN . Tanto la biblioteca cliente como el servidor conocerán el conjunto de caracteres si lo hace, y así las cosas funcionarán como deberían.
// The key is the "charset=utf8" part.
$dsn = ''mysql:host=localhost;dbname=testdb;charset=utf8'';
$dbh = new PDO($dsn, ''user'', ''pass'');
Pero el escape incorrecto de cuerdas 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 llamado ütube
(observe la diéresis), y cambie de latin
a utf8
través de nombres de conjuntos, y luego intente con $stmt->bindColumn(''ütube'', $var);
con ütube
siendo una cadena codificada en 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.
Al leer "MySQL de alto rendimiento" de O''Reilly, me he tropezado con lo siguiente
Otra consulta de basura común es SET NAMES UTF8, que es la forma incorrecta de hacer las cosas de todos modos (no cambia el conjunto de caracteres de la biblioteca del cliente, sino que afecta solo al servidor).
Estoy un poco confundido, porque solía poner "SET NAMES utf8" en la parte superior de cada script para que el db sepa que mis consultas están codificadas en utf8.
¿Alguien puede comentar la cita anterior o, para decirlo de manera más formal, cuáles son sus sugerencias / mejores prácticas para garantizar que el flujo de trabajo de mi base de datos sea compatible con Unicode?
Mis idiomas de destino son php y python si esto es relevante.
No estoy seguro acerca de py, pero php ahora tiene mysql_set_charset() , que establece que esta es la "forma preferida de cambiar el juego de caracteres [y] usar mysql_query () para ejecutar SET NAMES no se recomienda". Tenga en cuenta que esta función se introdujo para MySQL 5.0.7, por lo que no funcionará con versiones anteriores.
mysql_set_charset(''utf8'', $link);
Donde $ link es una conexión creada con mysql_connect
mysql_set_charset()
sería una opción, pero una opción limitada a ext/mysql
. Para ext/mysqli
es mysqli_set_charset
y para PDO
::mysql
necesita especificar un parámetro de conexión.
Como el uso de esta función da como resultado una llamada API de MySQL, se debe considerar mucho más rápido que la emisión de una consulta.
Con respecto al rendimiento, la forma más rápida de garantizar una comunicación basada en UTF-8 entre su secuencia de comandos y el servidor MySQL es configurar el servidor MySQL correctamente. Como SET NAMES x
es equivalent a
SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
mientras que SET character_set_connection = x
internamente también ejecuta SET collation_connection = <<default_collation_of_character_set_x>>
también puede establecer estas variables de servidor estáticamente en su my.ini/cnf
.
Tenga en cuenta los posibles problemas con otras aplicaciones que se ejecutan en la misma instancia de servidor MySQL y que requieren algún otro conjunto de caracteres.