PHP
PDO
charset
SQL
database connection

PHP PDO charset, set names?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

When a PHP application talks to MySQL through PDO, the connection character set matters as much as the table definition. If the client and server disagree about encoding, text can arrive garbled, comparisons can behave strangely, and characters outside basic ASCII may be lost or misread.

Prefer Charset In The DSN

For modern MySQL PDO usage, the normal answer is to set the character set in the DSN and use utf8mb4 rather than old three-byte utf8.

php
1<?php
2$dsn = 'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4';
3$options = [
4    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
5    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
6];
7
8$pdo = new PDO($dsn, 'app_user', 'secret', $options);
9
10$stmt = $pdo->prepare('INSERT INTO messages(body) VALUES (?)');
11$stmt->execute(["Hello 👋"]);

This is the cleanest approach because the connection starts with the expected encoding instead of negotiating it afterward with a manual SQL statement.

Why SET NAMES Is Usually Not The First Choice

Developers often ask whether they should run SET NAMES utf8 after connecting. It can work, but it is usually a fallback rather than the preferred solution.

The reason is simple: if PDO and the driver already support charset in the DSN, that setting is more direct and less error-prone. You avoid one extra command, and the connection is configured correctly from the beginning.

If you must support an older environment where DSN charset handling is not available or not reliable, then an initialization command is a practical workaround:

php
1<?php
2$dsn = 'mysql:host=127.0.0.1;dbname=app';
3$options = [
4    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
5    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
6];
7
8$pdo = new PDO($dsn, 'app_user', 'secret', $options);

That said, if your platform supports DSN charset cleanly, prefer the first pattern.

utf8 Versus utf8mb4

A lot of older examples still show utf8. In MySQL, that name historically means a three-byte subset, not full UTF-8. If you want reliable support for emoji and the full Unicode range, use utf8mb4 for the connection, the database, the tables, and the relevant text columns.

Connection settings alone are not enough if the schema still uses the wrong character set. Good encoding behavior requires consistency across the full stack.

Check The Whole Path

If text is still corrupted after fixing PDO, inspect the other layers too:

  • Database and table collation.
  • Column definitions.
  • HTML page encoding.
  • JSON serialization and HTTP headers.
  • Legacy data already stored with the wrong encoding.

It is common to fix the connection and then discover the real issue was an old table still defined with a mismatched collation.

Prepared statements do not solve charset problems by themselves. They help with SQL injection and query correctness, but the bytes still travel over the connection using the charset negotiated for that session. That is why connection charset and schema charset both matter, even in fully parameterized code.

That end-to-end consistency is what prevents the classic mojibake debugging spiral.

Common Pitfalls

One common mistake is using utf8 and assuming it means full UTF-8 support. In MySQL, that often fails for four-byte characters such as emoji.

Another mistake is relying on SET NAMES while ignoring the rest of the schema. A correct connection charset cannot fully compensate for misconfigured tables or columns.

A third issue is applying a manual SET NAMES statement everywhere out of habit. If DSN charset is available, that is usually clearer and easier to maintain.

Summary

  • With PDO and MySQL, prefer charset=utf8mb4 in the DSN.
  • Use SET NAMES only as a compatibility fallback when DSN charset is not sufficient.
  • 'utf8mb4 is the safer modern choice for full Unicode support.'
  • Debug charset issues across the entire path, not just the initial PDO connection.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.