必须将CI3数据库配置、MySQL服务端、表结构及HTML输出四层统一设为utf8mb4,否则emoji和生僻中文会乱码或丢失;仅改PHP配置无效,需同步调整my.cnf、建表语句、连接参数及页面声明。

确认数据库连接层是否用了 utf8mb4
CI3 默认只配 utf8,但 MySQL 5.5.3+ 实际需要 utf8mb4 才能存 emoji 和部分生僻中文。如果表结构或数据里有这类内容,光改 PHP 配置没用。
-
$db['default']['char_set']必须设为'utf8mb4',不能是'utf8' -
$db['default']['dbcollat']推荐设为'utf8mb4_unicode_ci'(比_general_ci更严谨) - 仅改 CI 配置不生效:MySQL 服务端也得支持——检查
my.cnf中[mysqld]段有没有character-set-server=utf8mb4 - 连上后执行
SHOW VARIABLES LIKE 'character_set%';,重点看character_set_client、character_set_connection、character_set_results三者是否都是utf8mb4
检查表和字段实际存储编码
CI 配置对了,但表本身还是 utf8 或 latin1,写进去的中文在磁盘上就是错的字节,后续怎么读都救不回来。
- 用
SHOW CREATE TABLE your_table;看建表语句末尾有没有DEFAULT CHARSET=utf8mb4 - 单列乱码?执行
SHOW FULL COLUMNS FROM your_table;,确认Collation列值是utf8mb4_unicode_ci类型 - 已有表要改:先备份,再跑
ALTER TABLE your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 注意:
CONVERT TO会重写所有字段,如果字段含TEXT且长度超限,可能报错,此时需分步MODIFY
验证数据从写入到读取全程未被二次转码
乱码常发生在“写对了,但读的时候又被某层按错编码解释”。CI3 的 $this->db->query() 和 Active Record 行为略有差异,容易踩坑。
- 避免混用:不要在
$this->db->set()->insert()之后,又用原生query("INSERT ...")且手动拼接中文——后者不会自动 escape 编码 - 查出的数据如果是数组,直接
echo $row['title'];出现乱码,先var_dump($row['title']);看原始字节:如果显示b"xxxx"或一堆\xe4\xb8\xad\xe6\x96\x87,说明数据本身是对的,问题在输出层;如果显示"涓枃"这类,说明写入时就错了 - HTML 输出前别漏掉
<meta charset="utf-8">,否则浏览器按 ISO-8859-1 解释 UTF-8 字节,必然变问号或方块
绕过 CI 自动处理,直连 MySQL 验证底层行为
当怀疑是 CI 层转码逻辑干扰时,跳过框架,用原生 mysqli 测试——这是定位“是 CI 的锅还是环境的锅”的最快方式。
- 写个临时脚本:
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'db');→$mysqli->set_charset('utf8mb4');→ 执行插入和查询,看结果是否正常 - 如果原生 OK,CI 不 OK:大概率是
database.php里char_set没生效,或被其他 config 覆盖(比如某些插件动态修改了$db数组) - 如果原生也乱码:说明 MySQL 服务端或客户端配置没对齐,回退到第一、二节再逐项核对
- 特别注意:Docker 环境下,PHP 容器和 MySQL 容器的时区、locale、字符集环境变量可能不一致,
docker-compose.yml里要显式声明environment: - MYSQL_COLLATION_SERVER=utf8mb4_unicode_ci
真正卡住的地方,往往不是某一行配置写错了,而是 MySQL 服务端、CI 连接参数、表结构、HTML 声明这四层中,有两层用了 utf8,另两层用了 utf8mb4,彼此“以为对方懂自己”,结果字节流在中间被无声截断或错解。每次改完,都要用 SHOW VARIABLES 和 SHOW CREATE TABLE 实锤验证,而不是只信配置文件。


















