php执行sql前不显式设置utf8mb4编码是中文乱码主因,必须在连接后、查询前调用set_charset("utf8mb4")或dsn中指定charset=utf8mb4,且需确保mysql服务端character_set_server为utf8mb4。

PHP执行SQL语句前不显式设置编码,90%以上中文乱码问题就已埋下伏笔——不是SQL写错了,而是连接层默认用latin1或系统 locale 解析 UTF-8 字节流,导致 INSERT 插入的是乱码字节,SELECT 查出来也是错的。
mysqli_connect 后必须调用 set_charset()
这是最直接、最可靠的设置方式,且必须在 mysqli_select_db() 之后、任何 SQL 执行之前调用。
-
set_charset("utf8mb4")是当前事实标准:它支持 emoji 和所有 Unicode 字符,而旧版utf8实际只支持 BMP 平面(3 字节),遇到 4 字节字符会截断报错 - 不能依赖
mysqli_query($conn, "SET NAMES utf8mb4")—— 它虽能临时生效,但不保证连接握手阶段的初始字符集,某些 MySQL 版本或代理(如 ProxySQL)下会失效 - 如果返回
false,说明服务器不支持该编码,需检查 MySQL 配置中是否启用了utf8mb4(SHOW VARIABLES LIKE 'character_set%')
$conn = new mysqli("localhost", "user", "pass", "db");
if ($conn->connect_error) die("连接失败: " . $conn->connect_error);
$conn->set_charset("utf8mb4"); // ✅ 关键一步
$result = $conn->query("SELECT name FROM users WHERE name = '张三'");
PDO 构造 DSN 时直接指定 charset
比运行时 exec('SET NAMES ...') 更安全,因为字符集在连接建立之初就协商完成,避免了中间状态。
- DSN 中的
charset=utf8mb4是 PDO MySQL 驱动原生支持的参数,无需额外exec() - 不要混用:如果 DSN 已含
charset,再执行$pdo->exec("SET NAMES utf8mb4")属于冗余,且可能在某些环境下触发重复设置警告 - 若使用长连接(
PDO::ATTR_PERSISTENT => true),仍需确保每次复用连接后字符集未被意外重置(实际中 PDO 通常保持,但测试建议加断言)
$dsn = "mysql:host=localhost;dbname=test;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
// ✅ 不需要再 $pdo->exec("SET NAMES ...")
$stmt = $pdo->prepare("INSERT INTO logs(msg) VALUES(?)");
$stmt->execute(["用户登录:✅"]);
PostgreSQL 用 pg_set_client_encoding()
MySQL 的 SET NAMES 在 PostgreSQL 中对应的是客户端编码设定,且必须在查询前调用,否则即使数据库是 UTF8,PHP 传入的字节也会被按默认编码(如 SQL_ASCII)解析。
- 参数必须是 PostgreSQL 支持的编码名,
"UTF8"(全大写)是标准写法,"utf8"或"utf-8"可能失败 - 函数返回
0表示成功,-1表示失败(例如服务器不支持该编码或连接已断开) - 注意:PHP 8.1+ 要求显式传入
pgsql\Connection对象,不能再依赖隐式默认连接
$conn = pg_connect("host=localhost dbname=test");
if (!$conn) die("PG 连接失败");
if (pg_set_client_encoding($conn, "UTF8") === -1) {
die("设置客户端编码失败");
}
$result = pg_query($conn, "SELECT title FROM articles WHERE title ILIKE '%PHP%'");
SQL Server 用 CharacterSet 连接选项
sqlsrv 扩展不通过 SQL 命令设编码,而是在连接参数中声明,这是微软驱动的硬性要求。
-
"CharacterSet" => "UTF-8"必须全大写UTF-8,写成utf8或UTF8都无效 - 仅设此参数还不够:表字段必须是
nvarchar/nchar,否则即使传输是 UTF-8,SQL Server 仍按单字节存储,中文变问号 - 如果用 PDO_SQLSRV,DSN 中不支持
charset参数,必须走连接选项数组方式
$server = "localhost";
$options = [
"Database" => "test",
"Uid" => "sa",
"PWD" => "password",
"CharacterSet" => "UTF-8" // ✅ 唯一有效方式
];
$conn = sqlsrv_connect($server, $options);
if (!$conn) die(print_r(sqlsrv_errors(), true));
// 后续所有 query/execute 自动按 UTF-8 处理
真正容易被忽略的是:数据库服务端本身的字符集配置。哪怕 PHP 层面全部设对了,如果 MySQL 的 character_set_server 是 latin1,或者 PostgreSQL 的数据库 ENCODING 是 SQL_ASCII,那客户端再怎么设,最终存进去的还是错的字节——务必先确认服务端编码已统一为 UTF8/UTF8MB4/UTF8。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











