mysqli_real_escape_string是PHP中用于转义SQL字符串特殊字符的函数,但因无法防护数字型注入、LIKE通配符、字符集错配等问题,现已不推荐单独用于防SQL注入;应改用预处理语句实现代码与数据隔离。

mysqli_real_escape_string 在 PHP 最新版(8.3+)中依然存在,但不能单独用来防 SQL 注入——它只是个转义工具,不是安全方案。直接拼接 SQL 字符串,哪怕每个变量都过一遍 mysqli_real_escape_string,只要漏一个、引号错位、数字型参数没兜住,就可能被绕过。
为什么 mysqli_real_escape_string 现在基本不推荐用
它只解决“字符串里有单引号怎么办”这一小段问题,但现代注入攻击早就不靠单引号了:
- 数字型参数如
id=123 OR 1=1不需要引号,mysqli_real_escape_string对它完全无效 - LIKE 查询里的
%和_不会被转义,用户输%admin%就可能查出不该看的数据 - 字符集没设对(比如没调
mysqli_set_charset($mysqli, 'utf8mb4')),某些多字节编码下可被绕过 - 连接断开时它返回
false,不判空直接拼进 SQL 会变成WHERE name = ''或更糟
它的定位是“辅助工具”,不是“防护层”。真要防注入,得换到语句级隔离。
mysqli_real_escape_string 正确调用的三个硬条件
就算你非要用它(比如维护老项目),下面三点缺一不可:
立即学习“PHP免费学习笔记(深入)”;
-
$mysqli必须是已成功连接且未关闭的对象,不能是null或假值;否则报Fatal error: Call to a member function real_escape_string() on null - 必须提前设置字符集:
$mysqli->set_charset('utf8mb4')或mysqli_set_charset($mysqli, 'utf8mb4');默认字符集不保证生效 - 输入必须是字符串类型;传
null、array或int会静默转成空字符串,逻辑可能意外跑偏
示例正确写法:
$mysqli = new mysqli($host, $user, $pass, $db);
$mysqli->set_charset('utf8mb4'); // 关键!
if ($mysqli->connect_error) die('connect failed');
<p>$name = $_POST['name'] ?? '';
if (is_string($name)) {
$safe_name = $mysqli->real_escape_string($name);
$sql = "SELECT * FROM users WHERE name = '$safe_name'";
}
替代方案:该用什么?
PHP 最新版里,唯一靠谱的路径是预处理语句(prepared statement):
- 面向对象写法:
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? AND status = ?"),再用bind_param() - 过程式写法:
$stmt = mysqli_prepare($mysqli, "..."),配合mysqli_stmt_bind_param() - 它把 SQL 结构和数据彻底分开,参数根本不会进 SQL 解析器,
OR 1=1、' OR '1'='1全部失效 - 数字、字符串、NULL 都自动适配,不用手动判断类型或加引号
注意:PDO 的 PDO::prepare() 也是同等级方案,但如果你已经用 mysqli,没必要为换而换。
如果非要处理 LIKE 查询里的用户输入
mysqli_real_escape_string 默认不转义 % 和 _,所以不能直接用于 LIKE。此时要么:
- 用
str_replace()手动补:$like_val = str_replace(['%', '_'], ['\%', '\_'], $input),再在 SQL 里加ESCAPE '\' - 或者改用
mb_ereg_replace()(需启用 mbstring)统一处理所有危险字符,包括%、_、\0、\n等
但最稳的方式仍是预处理 + CONCAT('%', ?, '%'),让数据库自己处理通配逻辑,不暴露 escape 规则给 PHP 层。
真正容易被忽略的是字符集设置和连接存活检查——这两点一漏,mysqli_real_escape_string 就从“有点弱”变成“完全失效”。



















