导读:本期聚焦于落伍者创作的《PHP设置MySQL字符集:mysqli_set_charset与SET NAMES哪种方式更安全?》,敬请观看详情。为什么直接执行SET NAMES utf8mb4可能导致乱码甚至注入漏洞?PHP操作MySQL时,字符集设置有两种常见做法:一是用SQL语句SET NAMES,二是调用mysqli_set_charset函数。两者表面上效果相似,底层却有关键差异。本文从连接层与服务器层的字符集转换机制入手,分析SET NAMES不通知客户端库带来的隐患,例如latin1连接下的宽字节注入风险,并对比两种方式在转义函数、预处理语句配合上的表现,最后给出utf8mb4的推荐配置,帮助你彻底解决中文乱码与安全问题。

PHP连接MySQL时如果字符集没设置对,轻则中文乱码,重则留下SQL注入漏洞。设置字符集最常见的两种方式是执行SQL语句SET NAMES utf8mb4,或者调用mysqli_set_charset()函数。很多教程里两者混用,仿佛随便选一个就行,但它们的行为并不完全等价。这篇文章详细对比这两种方式的底层差异,说明为什么官方文档明确推荐使用mysqli_set_charset()。

PHP设置MySQL字符集:mysqli_set_charset与SET NAMES哪种方式更安全?

两种方式分别做了什么

先看第一种方式,直接执行SQL语句:

<?php
// 方式一:通过SQL语句设置字符集
$conn = mysqli_connect('127.0.0.1', 'root', 'pass', 'test');
mysqli_query($conn, "SET NAMES utf8mb4");
?>

SET NAMES utf8mb4告诉MySQL服务器三件事:客户端发送的数据用的是utf8mb4编码,服务器返回的结果也用utf8mb4编码,两者之间的连接层字符集是utf8mb4。注意这只是通知了服务器端,PHP这一侧的mysqli客户端库对这件事一无所知。

再看第二种方式:

<?php
// 方式二:调用专用函数设置字符集
$conn = mysqli_connect('127.0.0.1', 'root', 'pass', 'test');
mysqli_set_charset($conn, 'utf8mb4');
?>

mysqli_set_charset()在内部同样会发送SET NAMES,但它多做了一件关键的事:同步更新了mysqli客户端库内部记录的字符集状态。这个差异看似不起眼,实际影响很大,主要体现在转义函数和预处理语句的行为上。

关键差异:转义函数依赖客户端字符集状态

问题出在mysqli_real_escape_string()这类函数上。这个函数在PHP侧对字符串做转义,它必须知道当前连接使用什么字符集,才能正确判断哪些字节需要转义。当字符集状态不同步时,转义就会出错。

举个例子,如果连接建立时没有设置字符集,默认可能是latin1。此时你执行了SET NAMES gbk,服务器端认为连接是GBK编码,但mysqli客户端库仍然以为自己是latin1。当调用mysqli_real_escape_string()时,它按latin1的规则处理字符串,而服务器按GBK解释,规则不匹配就会出现漏转义。

最典型的案例就是宽字节注入。GBK等双字节编码中,某些字符的第二个字节范围覆盖了0x5C(也就是反斜杠)。攻击者提交特殊构造的字节序列,转义函数插入的转义符会被吞掉,恶意引号就能逃逸出来。来看一段有漏洞的代码:

<?php
// 有隐患的写法
$conn = mysqli_connect('127.0.0.1', 'root', 'pass', 'test');
mysqli_query($conn, "SET NAMES gbk");

// 客户端库不知道当前是gbk,转义可能不正确
$id = mysqli_real_escape_string($conn, $_GET['id']);
$sql = "SELECT * FROM users WHERE id = '$id'";
mysqli_query($conn, $sql);
?>

如果把mysqli_query($conn, "SET NAMES gbk")换成mysqli_set_charset($conn, 'gbk'),转义函数就能感知到真实的字符集,按正确规则处理输入,宽字节注入的风险随之消除。这就是官方文档反复强调不要用SET NAMES语句的根本原因。

预处理语句为何能绕开这个坑

有人会问:既然转义函数有风险,预处理语句是不是也受影响?答案是不受影响,但原因值得说清楚。

预处理语句的参数是单独传输的,MySQL协议层面会明确标记参数的边界,值永远不会被解析成SQL语法的一部分,所以不存在引号逃逸的问题。这也是为什么现代PHP开发中普遍推荐PDO或mysqli的预处理方式来处理用户输入。

<?php
$conn = mysqli_connect('127.0.0.1', 'root', 'pass', 'test');
mysqli_set_charset($conn, 'utf8mb4');

$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param('s', $_GET['user']);
$stmt->execute();
$result = $stmt->get_result();
?>

不过要注意,预处理语句只解决了注入问题,不解决乱码问题。字符集如果不统一,中文数据照样可能变成问号或乱码。所以无论用不用预处理,第一步都应该用mysqli_set_charset()把连接字符设置好。

utf8与utf8mb4的选择及完整配置建议

聊字符集绕不开utf8和utf8mb4的区别。MySQL中的utf8实际是最多三个字节的阉割版,无法存储Emoji等四字节字符。utf8mb4才是完整的UTF-8实现。现在的项目应该统一使用utf8mb4,避免用户昵称带个表情符号就插入失败。

除了连接层,还要保证库、表、字段的字符集一致,混合字符集会导致隐式转换,既影响性能又可能产生乱码。可以在建库建表时明确指定:

-- 建库时指定字符集和排序规则
CREATE DATABASE app DEFAULT CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

-- 建表时同样明确指定
CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    COLLATE=utf8mb4_unicode_ci;

一个完整的PHP连接配置示例如下,供参考:

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

$conn = mysqli_init();
// 使用正确的字符集建立连接
mysqli_options($conn, MYSQLI_SET_CHARSET_NAME, 'utf8mb4');
mysqli_real_connect($conn, '127.0.0.1', 'root', 'pass', 'app');
?>

总结一下:两者在纯查询场景下效果一样,但只要涉及mysqli_real_escape_string(),就必须用mysqli_set_charset()。多花一个函数调用,换的是转义逻辑的正确性和安全性,这笔账怎么算都划算。再配合预处理语句和utf8mb4统一编码,中文乱码和字符集注入问题就能从根源上避免。

mysqli_set_charsetSET NAMES字符集修改时间:2026-09-09 19:00:49

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0909/53546.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。