PHP PDO: zestaw znaków, ustawić nazwy?

189

Miałem to wcześniej w moim normalnym połączeniu mysql_ *:

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

Czy potrzebuję go do PDO? A gdzie powinienem to mieć?

$connect = new PDO("mysql:host=$host;dbname=$db", $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Karem
źródło
10
Należy unikać „SET NAMES utf8” z powodu wstrzyknięcia SQL. Szczegółowe informacje można znaleźć na stronie php.net/manual/en/mysqlinfo.concepts.charset.php .
masakielastic
jeśli masz problemy z kodowaniem, możesz nie mieć innego wyjścia, jak ustawić utf8. Myślę, że na wynos należy użyć ciągu połączenia, jak pokazano poniżej Cobra_Fast . Użyj PDO :: przygotuj, aby przygotować instrukcje SQL z powiązanymi parametrami.
user12345
1
@masakielastic, a więc jak określić sortowanie jako „SET NAMES utf8 COLLATE utf8_unicode_ci”
datasn.io

Odpowiedzi:

460

Będziesz miał go w swoim ciągu połączenia, takim jak:

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

JEDNAK przed wersją PHP 5.3.6 opcja charset była ignorowana. Jeśli używasz starszej wersji PHP, musisz to zrobić w następujący sposób:

$dbh = new PDO("mysql:$connstr",  $user, $password);
$dbh->exec("set names utf8");
Cobra_Fast
źródło
15
Warto zauważyć, że to zachowanie zmieniło się w 5.3.6 i teraz działa poprawnie.
igorw
15
powinieneś być utf8 zamiast UTF-8 "mysql: host = $ host; dbname = $ db; charset = utf8"
od3n
3
Zignoruj ​​poniższe odpowiedzi, jeśli używasz aktualnej wersji PHP: działa dobrze w php 5.3.8.
kasimir
4
Czy w tym przypadku muszę również określić sortowanie? Takich jak „SET NAMES utf8 COLLATE utf8_unicode_ci”?
datasn.io
2
Dzięki! Uratowałem mój dzień!
Aleksandr,
64

Przed PHP 5.3.6 opcja charset była ignorowana. Jeśli używasz starszej wersji PHP, musisz to zrobić w następujący sposób:

<?php

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

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

?>
9nix00
źródło
1
Uwaga do modów: To jest poprawna odpowiedź i została opublikowana na rok przed zaakceptowaniem odpowiedzi, w której dokonano edycji tych informacji.
dotancohen
42

Jest to prawdopodobnie najbardziej elegancki sposób na zrobienie tego.
Bezpośrednio w wywołaniu konstruktora PDO, ale unikając opcji błędnego zestawu znaków (jak wspomniano powyżej):

$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"
  )
);

Działa świetnie dla mnie.

Jpsy
źródło
1
Rozumiem, że jest to również błąd dla php 5.3.0. W takim przypadku należy wprowadzić opcję w tablicy przez odniesienie jej numer, a nie jego nazwę tak: array(1002 => 'SET NAMES utf8',...).
JDelage
Dzięki za podpowiedź! Z powodzeniem używam powyższego kodu na wielu systemach produkcyjnych z różnymi wersjami PHP w wersji 5.3.X, ale tak naprawdę żaden z nich nie jest 5.3.0.
Jpsy
4
Moim zdaniem może być bardziej elegancki bez opcji specyficznych dla bazy danych
Aalex Gabi
To prawda, że ​​MYSQL_ATTR_INIT_COMMAND jest dostępny tylko dla baz danych MySQL (dostępne komendy dla każdego typu db znajdują się na podstronach php.net/manual/de/pdo.drivers.php ). Ale właśnie o to prosił PO.
Jpsy,
przekazywanie charset=utf8ciągu dsn działa! Próbowałem rozwiązać ten problem pod adresem groups.google.com/d/msg/auraphp/syMS26Rz-q8/9laQr9tR4EoJ
Hari KT
16

Dla kompletności istnieją trzy sposoby ustawienia kodowania podczas łączenia się z MySQL z PDO, a które są dostępne, zależą od twojej wersji PHP. Kolejność preferencji byłaby następująca:

  1. charset parametr w ciągu DSN
  2. Uruchom SET NAMES utf8z PDO::MYSQL_ATTR_INIT_COMMANDopcją połączenia
  3. Uruchom SET NAMES utf8ręcznie

Ten przykładowy kod implementuje wszystkie trzy:

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

Wykonanie wszystkich trzech czynności jest prawdopodobnie przesadą (chyba że piszesz zajęcia, które planujesz rozpowszechniać lub wykorzystywać ponownie).

Álvaro González
źródło
1
Czy istnieje odpowiednik ODBC / Access? Mam teraz działające połączenie UTF8 Oracle i MySQL PHP PDO, ale nie mogę uruchomić go dla ODBC / Access.
Jan
2
Aha i nigdy NIE OKREŚLAJ hasła do bazy danych. Są tak globalne jak superglobale, a to nie jest dobra rzecz, gdy pracujesz z hasłami.
Xesau,
2

Chcę tylko dodać, że musisz upewnić się, że twoja baza danych jest tworzona przy użyciu COLLATE utf8_general_ci lub dowolnego sortowania, którego chcesz użyć, w przeciwnym razie możesz skończyć z inną niż zamierzona.

W phpmyadmin możesz zobaczyć sortowanie, klikając bazę danych i wybierając operacje. Jeśli spróbujesz utworzyć tabele z innym zestawieniem niż baza danych, tabele i tak zakończą się zestawieniem bazy danych.

Upewnij się więc, że sortowanie bazy danych jest prawidłowe przed utworzeniem tabel. Mam nadzieję, że to oszczędza komuś kilka godzin lol

Medda86
źródło
1
„Jeśli spróbujesz utworzyć tabele z innym zestawieniem niż baza danych, tabele i tak poskutkują zestawieniem bazy danych” - nie sądzę, aby było to poprawne. Zestawienie tabel ma pierwszeństwo przed zestawieniem w bazie danych. dev.mysql.com/doc/refman/5.5/en/charset-table.html
humble_wolf
2

Myślę, że potrzebujesz dodatkowego zapytania, ponieważ opcja charset w DSN jest faktycznie ignorowana. patrz link opublikowany w komentarzu do innej odpowiedzi.

Patrząc, jak Drupal 7 robi to w 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
źródło
1
$conn = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);
gauravbhai daxini
źródło
1
Chociaż ta odpowiedź jest prawdopodobnie poprawna i przydatna, najlepiej jest dołączyć wraz z nią wyjaśnienie wyjaśniające, w jaki sposób pomaga rozwiązać problem. Staje się to szczególnie przydatne w przyszłości, jeśli nastąpi zmiana (prawdopodobnie niezwiązana), która spowoduje, że przestanie ona działać, a użytkownicy będą musieli zrozumieć, jak kiedyś działała.
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
źródło
0

Testuję ten kod i

$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'];
}
elita andot
źródło
Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, dlaczego i / lub jak ten kod odpowiada na pytanie, poprawia jego długoterminową wartość.
rollstuhlfahrer
-1

$ con = nowy PDO („mysql: host = $ dbhost; dbname = $ baza danych; charset = $ encoding ”, „$ dbuser”, „$ dbpassword”);

Dilmurod
źródło