218

I had this previously in my normal mysql_* connection:

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

Do I need it for the PDO? And where should I have it?

$connect = new PDO("mysql:host=$host;dbname=$db", $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
Karem
  • 17,615
  • 72
  • 178
  • 278
  • 10
    "SET NAMES utf8" should be avoided because of SQL injection. See http://www.php.net/manual/en/mysqlinfo.concepts.charset.php for he details. – masakielastic Jun 08 '13 at 09:47
  • if you have charset issues then you may have no choice but to set to utf8. I think the take away should be use the [connection string as shown by Cobra_Fast](https://stackoverflow.com/a/4361485/1048805) below. Use PDO::prepare to prepare your SQL statements with bound parameters. – user12345 Jul 02 '14 at 23:16
  • 1
    @masakielastic, then how should we specify collation as "SET NAMES utf8 COLLATE utf8_unicode_ci" – datasn.io Sep 12 '14 at 11:17

10 Answers10

533

You'll have it in your connection string like:

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

HOWEVER, prior to PHP 5.3.6, the charset option was ignored. If you're running an older version of PHP, you must do it like this:

$dbh = new PDO("mysql:host=$host;dbname=$db",  $user, $password);
$dbh->exec("set names utf8mb4");
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Cobra_Fast
  • 15,671
  • 8
  • 57
  • 102
  • 16
    It's worth noting that this behaviour changed in 5.3.6, and is now working correctly. – igorw Nov 10 '11 at 10:47
  • 15
    shoudle be utf8 instaead of UTF-8 "mysql:host=$host;dbname=$db;charset=utf8" – od3n Feb 17 '12 at 03:33
  • 3
    Ignore the answers below if you are using an up-to-date version of PHP: it works just fine in php 5.3.8. – kasimir May 02 '12 at 15:02
  • If you're hosting your PHP site using Debian Squeeze with its standard PHP build, note that they've tied their PHP version to 5.3.3, so you'll need to call the `SET NAMES` query manually. – Spudley Sep 23 '13 at 11:09
  • 4
    Do I have to also specify collation in this case? Such as 'SET NAMES utf8 COLLATE utf8_unicode_ci'? – datasn.io Sep 12 '14 at 11:15
  • 4
    Switch from utf8 to utf8mb4. – Rick James Nov 30 '19 at 06:57
66

Prior to PHP 5.3.6, the charset option was ignored. If you're running an older version of PHP, you must do it like this:

<?php

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

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

?>
Don MacAskill
  • 868
  • 1
  • 7
  • 14
9nix00
  • 3,852
  • 2
  • 21
  • 27
  • 2
    Note to mods: This is the correct answer, and it was posted one year before the accepted answer had this information edited into it. – dotancohen Dec 24 '13 at 06:38
50

This is probably the most elegant way to do it.
Right in the PDO constructor call, but avoiding the buggy charset option (as mentioned above):

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

Works great for me.

Jpsy
  • 20,077
  • 7
  • 118
  • 115
  • 1
    My understanding is that this also is buggy for php 5.3.0. In that case you need to enter the option in the array by referencing its number rather than its name like this: `array(1002 => 'SET NAMES utf8',...)`. – JDelage Feb 24 '12 at 23:50
  • Thanks for the hint! I am using the above code successfully on multiple production systems running different 5.3.X versions of PHP, but actually none of them is 5.3.0. – Jpsy Feb 26 '12 at 16:08
  • 4
    I my opinion it could be more elegant without database specific options – Aalex Gabi Sep 06 '12 at 16:51
  • True, the MYSQL_ATTR_INIT_COMMAND is only available for MySQL databases (for available commands for each db type see the sub-pages of http://www.php.net/manual/de/pdo.drivers.php). But this is exactly what the OP has asked for. – Jpsy Nov 06 '12 at 11:54
  • passing `charset=utf8` in the dsn string works! I was trying to figure out the issue at https://groups.google.com/d/msg/auraphp/syMS26Rz-q8/9laQr9tR4EoJ – Hari K T Nov 14 '13 at 05:17
  • @HariKT: Whether it works or not depends on the version of PHP - see the answers of 9nix00 and Cobra_Fast. – Jpsy Nov 18 '13 at 08:28
  • @Jpsy Thank you for pointing me to it. Aura.Sql v1 only works for 5.4+ ;-). v2 in develop-2 branch will work for 5.3+. And I assume the question was for v1. Thank you. – Hari K T Nov 18 '13 at 09:43
16

For completeness, there're actually three ways to set the encoding when connecting to MySQL from PDO and which ones are available depend on your PHP version. The order of preference would be:

  1. charset parameter in the DSN string
  2. Run SET NAMES utf8 with PDO::MYSQL_ATTR_INIT_COMMAND connection option
  3. Run SET NAMES utf8 manually

This sample code implements all three:

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

Doing all three is probably overkill (unless you're writing a class you plan to distribute or reuse).

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • 1
    Is there an ODBC/Access equivalent? I now have a working Oracle and MySQL PHP PDO UTF8 connection but I can't get it working for ODBC/Access. – Jan May 28 '15 at 13:32
  • 2
    Oh and never DEFINE your database password. They're as global as superglobals, and that's not a good thing when you're you're working with passwords. – Xesau Aug 24 '15 at 19:56
  • 1
    I'm on PHP 7.4 and still had to run a separate query, in order to get non-latin characters displayed when getting the data `SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci`. The utf8mb4 in dsn wasn't enough – Moseleyi Jul 26 '21 at 20:34
4
$conn = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $pass);
Gauravbhai Daxini
  • 2,032
  • 2
  • 22
  • 28
  • 2
    While this answer is probably correct and useful, it is preferred if you include some explanation along with it to explain how it helps to solve the problem. This becomes especially useful in the future, if there is a change (possibly unrelated) that causes it to stop working and users need to understand how it once worked. – Erty Seidohl May 23 '18 at 22:03
3
$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 );   
Pang
  • 9,564
  • 146
  • 81
  • 122
2

I think you need an additionally query because the charset option in the DSN is actually ignored. see link posted in the comment of the other answer.

Looking at how Drupal 7 is doing it in 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');
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Berdir
  • 6,881
  • 2
  • 26
  • 38
1

I just want to add that you have to make sure your database is created with COLLATE utf8_general_ci or whichever collation you want to use, Else you might end up with another one than intended.

In phpmyadmin you can see the collation by clicking your database and choose operations. If you try create tables with another collation than your database, your tables will end up with the database collation anyways.

So make sure the collation for your database is right before creating tables. Hope this saves someone a few hours lol

Medda86
  • 1,582
  • 1
  • 12
  • 19
  • 1
    "If you try create tables with another collation than your database, your tables will end up with the database collation anyways" - I don't think it is correct. Table collation takes precedence over database's collation. https://dev.mysql.com/doc/refman/5.5/en/charset-table.html – humble_wolf Oct 07 '17 at 09:32
0

I test this code and

$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'];
}
GYaN
  • 2,327
  • 4
  • 19
  • 39
  • While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – rollstuhlfahrer Apr 06 '18 at 07:07
0

in my case, i had to add this line:

$conn->exec("set names utf8"); //Support utf8

How it will look:

$conn = new PDO("mysql:host=$servername;dbname=$db", $username, $password);
$conn->exec("set names utf8"); //Support utf8