PHP PDO: charset, establecer nombres?

189

Tenía esto anteriormente en mi conexión normal mysql_ *:

mysql_set_charset("utf8",$link);
mysql_query("SET NAMES 'UTF8'");

¿Lo necesito para la DOP? ¿Y dónde debería tenerlo?

$connect = new PDO("mysql:host=$host;dbname=$db", $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Karem
fuente
10
Se debe evitar "SET NAMES utf8" debido a la inyección de SQL. Ver php.net/manual/en/mysqlinfo.concepts.charset.php para más detalles.
masakielastic
Si tiene problemas con el juego de caracteres, es posible que no tenga más remedio que configurar en utf8. Creo que la eliminación debería ser usar la cadena de conexión como se muestra a continuación por Cobra_Fast . Use PDO :: prepare para preparar sus declaraciones SQL con parámetros enlazados.
usuario12345
1
@masakielastic, entonces ¿cómo debemos especificar la colación como "SET NAMES utf8 COLLATE utf8_unicode_ci"
datasn.io

Respuestas:

460

Lo tendrás en tu cadena de conexión como:

"mysql:host=$host;dbname=$db;charset=utf8"

SIN EMBARGO, antes de PHP 5.3.6, la opción charset fue ignorada. Si está ejecutando una versión anterior de PHP, debe hacerlo así:

$dbh = new PDO("mysql:$connstr",  $user, $password);
$dbh->exec("set names utf8");
Cobra_Fast
fuente
15
Vale la pena señalar que este comportamiento cambió en 5.3.6 y ahora funciona correctamente.
igorw
15
debe ser utf8 en lugar de UTF-8 "mysql: host = $ host; dbname = $ db; charset = utf8"
od3n
3
Ignore las respuestas a continuación si está utilizando una versión actualizada de PHP: funciona bien en php 5.3.8.
kasimir
44
¿Tengo que especificar también la intercalación en este caso? Tales como 'SET NAMES utf8 COLLATE utf8_unicode_ci'?
datasn.io
2
¡Gracias! Me salvó el día!
Aleksandr
64

Antes de PHP 5.3.6, se ignoraba la opción charset. Si está ejecutando una versión anterior de PHP, debe hacerlo así:

<?php

    $dbh = new PDO("mysql:$connstr",  $user, $password);

    $dbh -> exec("set names utf8");

?>
9nix00
fuente
1
Nota para los mods: esta es la respuesta correcta, y se publicó un año antes de que la respuesta aceptada tuviera esta información editada.
dotancohen
42

Esta es probablemente la forma más elegante de hacerlo.
Justo en la llamada del constructor PDO, pero evitando la opción de juego de caracteres con errores (como se mencionó anteriormente):

$connect = new PDO(
  "mysql:host=$host;dbname=$db", 
  $user, 
  $pass, 
  array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
  )
);

Funciona muy bien para mi.

Jpsy
fuente
1
Tengo entendido que esto también es defectuoso para php 5.3.0. En ese caso, es necesario introducir la opción de la matriz haciendo referencia a su número en lugar de su nombre como esto: array(1002 => 'SET NAMES utf8',...).
JDelage
¡Gracias por la pista! Estoy usando el código anterior con éxito en múltiples sistemas de producción que ejecutan diferentes versiones 5.3.X de PHP, pero en realidad ninguno de ellos es 5.3.0.
Jpsy
44
En mi opinión, podría ser más elegante sin opciones específicas de la base de datos
Aalex Gabi
Es cierto que MYSQL_ATTR_INIT_COMMAND solo está disponible para bases de datos MySQL (para ver los comandos disponibles para cada tipo de base de datos, consulte las subpáginas de php.net/manual/de/pdo.drivers.php ). Pero esto es exactamente lo que ha pedido el OP.
Jpsy
pasar charset=utf8la cadena dsn funciona! Estaba tratando de resolver el problema en groups.google.com/d/msg/auraphp/syMS26Rz-q8/9laQr9tR4EoJ
Hari KT
16

Para completar, en realidad hay tres formas de configurar la codificación cuando se conecta a MySQL desde PDO y cuáles están disponibles dependen de su versión de PHP. El orden de preferencia sería:

  1. charset parámetro en la cadena DSN
  2. Ejecutar SET NAMES utf8con PDO::MYSQL_ATTR_INIT_COMMANDopción de conexión
  3. Ejecutar SET NAMES utf8manualmente

Este código de ejemplo implementa los tres:

<?php

define('DB_HOST', 'localhost');
define('DB_SCHEMA', 'test');
define('DB_USER', 'test');
define('DB_PASSWORD', 'test');
define('DB_ENCODING', 'utf8');


$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_SCHEMA;
$options = array(
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
);

if( version_compare(PHP_VERSION, '5.3.6', '<') ){
    if( defined('PDO::MYSQL_ATTR_INIT_COMMAND') ){
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . DB_ENCODING;
    }
}else{
    $dsn .= ';charset=' . DB_ENCODING;
}

$conn = @new PDO($dsn, DB_USER, DB_PASSWORD, $options);

if( version_compare(PHP_VERSION, '5.3.6', '<') && !defined('PDO::MYSQL_ATTR_INIT_COMMAND') ){
    $sql = 'SET NAMES ' . DB_ENCODING;
    $conn->exec($sql);
}

Hacer las tres cosas es probablemente exagerado (a menos que esté escribiendo una clase que planea distribuir o reutilizar).

Álvaro González
fuente
1
¿Hay un equivalente de ODBC / Access? Ahora tengo una conexión Oracle y MySQL PHP PDO UTF8 en funcionamiento, pero no puedo hacer que funcione para ODBC / Access.
enero
2
Ah, y nunca defina la contraseña de su base de datos. Son tan globales como las superglobales, y eso no es bueno cuando trabajas con contraseñas.
Xesau
2

Solo quiero agregar que debe asegurarse de que su base de datos se cree con COLLATE utf8_general_ci o cualquier colación que desee usar, de lo contrario, podría terminar con otra que no sea la prevista.

En phpmyadmin puede ver la clasificación haciendo clic en su base de datos y seleccionando operaciones. Si intenta crear tablas con una clasificación diferente a su base de datos, sus tablas terminarán con la clasificación de la base de datos de todos modos.

Así que asegúrese de que la clasificación de su base de datos sea correcta antes de crear tablas. Espero que esto ahorre a alguien unas horas jajaja

Medda86
fuente
1
"Si intenta crear tablas con una clasificación diferente a la de su base de datos, sus tablas terminarán con la clasificación de la base de datos de todos modos" - No creo que sea correcto. La clasificación de tablas tiene prioridad sobre la clasificación de la base de datos. dev.mysql.com/doc/refman/5.5/en/charset-table.html
humble_wolf
2

Creo que necesita una consulta adicional porque la opción de juego de caracteres en el DSN se ignora realmente. ver enlace publicado en el comentario de la otra respuesta.

Mirando cómo Drupal 7 lo está haciendo en http://api.drupal.org/api/drupal/includes--database--mysql--database.inc/function/DatabaseConnection_mysql%3A%3A__construct/7 :

// Force MySQL to use the UTF-8 character set. Also set the collation, if a
// certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
// for UTF-8.
if (!empty($connection_options['collation'])) {
  $this->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']);
}
else {
  $this->exec('SET NAMES utf8');
}
Berdir
fuente
1
$conn = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);
gauravbhai daxini
fuente
1
Si bien esta respuesta es probablemente correcta y útil, se prefiere si incluye alguna explicación junto con ella para explicar cómo ayuda a resolver el problema. Esto se vuelve especialmente útil en el futuro, si hay un cambio (posiblemente no relacionado) que hace que deje de funcionar y los usuarios necesitan entender cómo funcionó una vez.
Erty Seidohl
1
$con="";
$MODE="";
$dbhost = "localhost";
$dbuser = "root";
$dbpassword = "";
$database = "name";

$con = new PDO ( "mysql:host=$dbhost;dbname=$database", "$dbuser", "$dbpassword", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
$con->setAttribute ( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );   
Jayanit Satani
fuente
0

Pruebo este código y

$db=new PDO('mysql:host=localhost;dbname=cwDB','root','',
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
$sql="select * from products  ";
$stmt=$db->prepare($sql);
$stmt->execute();
while($result=$stmt->fetch(PDO::FETCH_ASSOC)){                  
    $id=$result['id'];
}
andot elite
fuente
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
rollstuhlfahrer
-1

$ con = new PDO ("mysql: host = $ dbhost; dbname = $ database; charset = $ encoding ", "$ dbuser", "$ dbpassword");

Dilmurod
fuente