PHP开发过程中,数据库乱码是很常见的坑,很多开发者在写入中文数据到数据库,或者从数据库读取中文内容展示到页面时,都会遇到字符显示成问号、乱码的情况。乱码的核心原因是整个数据流转链路中,不同环节的字符集设置没有统一,只要把每个环节的编码都对齐,就能彻底解决这个问题。

乱码问题的常见场景
常见的乱码场景主要有三类:
- PHP脚本写入中文数据到数据库后,数据库里存储的是乱码
- 数据库里存储的正常中文,读取到PHP页面后显示为乱码
- 不同页面之间传递中文参数,存入数据库后出现乱码
核心环节的字符集设置
1. PHP脚本文件本身的编码
首先要保证PHP脚本文件本身的编码是UTF-8无BOM格式,如果你用的是编辑器比如VS Code,可以在右下角查看文件编码,如果不是的话可以转换为UTF-8无BOM。如果脚本文件本身是GBK编码,即使后面数据库设置正确,也很容易出现乱码。
2. 数据库连接时的字符集设置
连接数据库之后,第一件事就是设置连接字符集,不同数据库扩展的设置方式略有区别。
使用mysqli扩展的设置方式
如果是用mysqli连接MySQL数据库,需要在连接成功后执行设置字符集的操作:
<?php
// 连接数据库
$mysqli = new mysqli('127.0.0.1', 'root', 'password', 'test_db');
// 检查连接是否成功
if ($mysqli->connect_error) {
die('连接失败:' . $mysqli->connect_error);
}
// 设置连接字符集为utf8mb4,支持所有Unicode字符包括emoji
if (!$mysqli->set_charset('utf8mb4')) {
die('设置字符集失败:' . $mysqli->error);
}
// 后续执行插入、查询操作
$sql = "INSERT INTO user (name) VALUES ('张三')";
$mysqli->query($sql);
$mysqli->close();
?>
使用PDO扩展的设置方式
如果用PDO连接数据库,可以在初始化PDO对象的时候直接指定字符集:
<?php
try {
// 连接字符串中指定charset为utf8mb4
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=test_db;charset=utf8mb4',
'root',
'password'
);
// 设置错误模式为异常,方便调试
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 执行插入操作
$sql = "INSERT INTO user (name) VALUES ('李四')";
$pdo->exec($sql);
} catch (PDOException $e) {
die('操作失败:' . $e->getMessage());
}
?>
3. 数据库、表、字段的字符集设置
数据库本身的字符集、表的字符集、字段的字符集都需要统一,建议全部设置为utf8mb4,而不是utf8,因为MySQL里的utf8是阉割版,只支持3字节的字符,无法存储emoji和部分生僻字。
创建数据库的时候指定字符集:
CREATE DATABASE test_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;
如果已经存在的数据库需要修改字符集,可以执行:
ALTER DATABASE test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
创建表的时候指定字符集:
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
4. 页面输出的字符集设置
PHP输出到浏览器的页面,也需要指定编码为UTF-8,可以在脚本开头用header函数设置:
<?php
// 设置页面输出编码为UTF-8,要在输出任何内容之前调用
header('Content-Type: text/html; charset=utf-8');
// 后续输出内容
echo '用户名称:张三';
?>
常见避坑点
- 不要使用
utf8作为MySQL的字符集,优先用utf8mb4,避免生僻字和emoji存储失败 - 设置连接字符集必须在执行任何SQL语句之前,否则之前的SQL可能已经用了默认字符集导致乱码
- HTML页面的<meta>标签也要设置编码,和PHP的header设置保持一致:<meta charset="utf-8">
- 如果之前已经出现了乱码数据,修改字符集之后不会自动修复已有的乱码数据,需要重新写入正确的数据
- 避免使用GBK编码的数据库和UTF-8的PHP脚本混用,尽量全链路统一用UTF-8相关编码
快速排查步骤
如果遇到乱码问题,可以按照以下步骤排查:
- 检查PHP脚本文件编码是否为UTF-8无BOM
- 检查数据库连接后是否设置了正确的字符集
- 检查数据库、表、字段的字符集是否为utf8mb4
- 检查页面header和meta标签的编码设置
- 打印数据库返回的原始数据,看是存储时就乱码还是输出时乱码