PHP PDO: charset, definir nomes?

188

Eu tinha isso anteriormente na minha conexão normal mysql_ *:

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

Preciso disso para a DOP? E onde devo tê-lo?

$connect = new PDO("mysql:host=$host;dbname=$db", $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Karem
fonte
10
"SET NAMES utf8" deve ser evitado devido à injeção de SQL. Veja php.net/manual/en/mysqlinfo.concepts.charset.php para obter detalhes.
masakielastic
Se você tiver problemas com o conjunto de caracteres, poderá não ter outra opção a não ser definir para utf8. Eu acho que o take away deve usar a string de conexão, como mostra Cobra_Fast abaixo. Use PDO :: prepare para preparar suas instruções SQL com parâmetros vinculados.
User12345
1
@masakielastic, então como devemos especificar agrupamento como "SET NAMES utf8 utf8_unicode_ci COLLATE"
datasn.io

Respostas:

457

Você o encontrará na cadeia de conexão como:

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

No entanto, antes do PHP 5.3.6, a opção charset era ignorada. Se você estiver executando uma versão mais antiga do PHP, faça o seguinte:

$dbh = new PDO("mysql:$connstr",  $user, $password);
$dbh->exec("set names utf8");
Cobra_Fast
fonte
15
Vale ressaltar que esse comportamento foi alterado na 5.3.6 e agora está funcionando corretamente.
Igorw
15
shoudle be utf8 instaead of UTF-8 "mysql: host = $ host; dbname = $ db; charset = utf8"
od3n
3
Ignore as respostas abaixo se você estiver usando uma versão atualizada do PHP: ele funciona perfeitamente no php 5.3.8.
kasimir
4
Também tenho que especificar agrupamento neste caso? Tais como 'SET NAMES utf8 COLLATE utf8_unicode_ci'?
Datasn.io
2
Obrigado! Salvou o meu dia!
Aleksandr
64

Antes do PHP 5.3.6, a opção charset era ignorada. Se você estiver executando uma versão mais antiga do PHP, faça o seguinte:

<?php

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

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

?>
9nix00
fonte
1
Nota para os mods: Esta é a resposta correta e foi postada um ano antes da resposta aceita ter essas informações editadas.
dotancohen
42

Esta é provavelmente a maneira mais elegante de fazer isso.
À direita na chamada do construtor PDO, mas evitando a opção de charset de buggy (como mencionado acima):

$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 muito bem para mim.

Jpsy
fonte
1
Meu entendimento é que isso também é buggy para o php 5.3.0. Nesse caso, você precisa digitar a opção na matriz fazendo referência a seu número, em vez de seu nome como este: array(1002 => 'SET NAMES utf8',...).
JDelage
Obrigado pela dica! Estou usando o código acima com êxito em vários sistemas de produção executando versões 5.3.X diferentes do PHP, mas na verdade nenhum deles é 5.3.0.
Jpsy
4
I minha opinião poderia ser mais elegante, sem opções específicas de banco de dados
Aalex Gabi
É verdade que o MYSQL_ATTR_INIT_COMMAND está disponível apenas para bancos de dados MySQL (para obter comandos disponíveis para cada tipo de banco de dados, consulte as subpáginas de php.net/manual/de/pdo.drivers.php ). Mas é exatamente isso que o OP solicitou.
Jpsy
passando charset=utf8na string dsn funciona! Eu estava tentando descobrir o problema em groups.google.com/d/msg/auraphp/syMS26Rz-q8/9laQr9tR4EoJ
Hari KT
16

Para completar, existem três maneiras de definir a codificação ao conectar-se ao MySQL a partir do PDO e quais estão disponíveis dependem da sua versão do PHP. A ordem de preferência seria:

  1. charset parâmetro na sequência DSN
  2. Executar SET NAMES utf8com PDO::MYSQL_ATTR_INIT_COMMANDopção de conexão
  3. Executar SET NAMES utf8manualmente

Este código de exemplo implementa todos os três:

<?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);
}

Realizar todos os três provavelmente é um exagero (a menos que você esteja escrevendo uma aula que planeja distribuir ou reutilizar).

Álvaro González
fonte
1
Existe um equivalente ODBC / Access? Agora eu tenho uma conexão Oracle e MySQL PHP PDO UTF8 funcionando, mas não consigo fazê-la funcionar para ODBC / Access.
Jan
2
Ah, e nunca DEFINE sua senha do banco de dados. Eles são tão globais quanto os superglobais, e isso não é uma coisa boa quando você trabalha com senhas.
Xesau
2

Eu só quero acrescentar que você precisa garantir que seu banco de dados seja criado com COLLATE utf8_general_ci ou qualquer outro agrupamento que você deseja usar. Caso contrário, você pode acabar com outro que não o pretendido.

No phpmyadmin, você pode ver o agrupamento clicando no seu banco de dados e escolhendo operações. Se você tentar criar tabelas com outro agrupamento que não o seu banco de dados, suas tabelas acabarão com o agrupamento de banco de dados de qualquer maneira.

Portanto, verifique se o agrupamento do seu banco de dados está correto antes de criar tabelas. Espero que isso salve alguém algumas horas lol

Medda86
fonte
1
"Se você tentar criar tabelas com outro agrupamento que não o seu banco de dados, suas tabelas acabarão com o agrupamento de banco de dados de qualquer maneira" - não acho que esteja correto. O agrupamento de tabelas tem precedência sobre o agrupamento do banco de dados. dev.mysql.com/doc/refman/5.5/en/charset-table.html
humble_wolf 7/07
2

Eu acho que você precisa de uma consulta adicional porque a opção charset no DSN é realmente ignorada. veja o link postado no comentário da outra resposta.

Examinando como o Drupal 7 está fazendo isso em 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
fonte
1
$conn = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);
gauravbhai daxini
fonte
1
Embora essa resposta seja provavelmente correta e útil, é preferível incluir algumas explicações para explicar como isso ajuda a resolver o problema. Isso se torna especialmente útil no futuro, se houver uma alteração (possivelmente não relacionada) que faça com que ele pare de funcionar e os usuários precisem entender como ela funcionou.
Erty Seidohl
0

Eu testei esse código e

$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'];
}
elite andot
fonte
Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
Rollstuhlfahrer
0
$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
fonte
-1

$ con = novo PDO ("mysql: host = $ dbhost; dbname = $ banco de dados; charset = $ encoding ", "$ dbuser", "$ dbpassword");

Dilmurod
fonte