
本文介绍如何使用 php 和 mysql 安全地实现“在原有字段值基础上增加新数值”的更新逻辑,重点解决 sql 注入风险,并推荐使用预处理语句替代拼接式 sql。
本文介绍如何使用 php 和 mysql 安全地实现“在原有字段值基础上增加新数值”的更新逻辑,重点解决 sql 注入风险,并推荐使用预处理语句替代拼接式 sql。
在库存管理系统等业务场景中,经常需要对数据库中已存在的数值型字段(如 inhouse)执行“增量更新”——即不覆盖原值,而是将用户提交的新数量与当前值相加后写回数据库。例如:数据库中 inhouse = 15,用户输入 5,最终应更新为 20。
但原始代码存在两个关键问题:
✅ 逻辑错误:SQL 语句 "UPDATE ... SET inhouse = '$new_quantity + $in'" 将 $new_quantity + $in 当作字符串拼接,而非数据库内计算;且 $in 变量在 PHP 处理层未定义(仅在 HTML 查询循环中存在),导致运行时错误或空值参与运算。
❌ 严重安全漏洞:直接拼接 $_POST 参数进入 SQL,极易遭受 SQL 注入攻击(如传入 '1; DROP TABLE inv_tbl--' 即可摧毁数据)。
✅ 正确做法:使用 MySQLi 预处理语句(Prepared Statements)
预处理语句将 SQL 结构与数据参数分离,由数据库引擎安全解析,彻底杜绝注入风险。核心步骤如下:
- 编写含占位符
?的 SQL(计算逻辑在数据库端完成); - 绑定 PHP 变量到对应占位符;
- 执行语句。
include("../../connection.php");
if (isset($_POST['update'])) {
$id = (int)$_POST['id']; // 强制类型转换,增强健壮性
$quantity_to_add = (int)$_POST['quantity'];
// ✅ 安全写法:在 SQL 中直接执行加法,无需先查再算
$stmt = $connections->prepare("UPDATE inv_tbl SET inhouse = inhouse + ? WHERE id = ?");
$stmt->bind_param("ii", $quantity_to_add, $id); // "ii" 表示两个整型参数
$stmt->execute();
addLog($connections, "Added stock: {$quantity_to_add} units to item ID {$id}");
header("Location: ../stocks.php");
exit;
}? 为什么不在 PHP 中先查询再更新?
若采用“SELECT → 计算 → UPDATE”三步法,高并发下可能因竞态条件(race condition)导致数据错乱(如两个请求同时读到inhouse=15,各自加5后都写入20,实际应为25)。而UPDATE ... SET field = field + ?是原子操作,由数据库保证线程安全。
? 关键安全实践补充
-
永远避免字符串拼接 SQL:包括
mysqli_query()中的变量插值; -
启用错误报告(开发环境):添加
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);可捕获预处理异常; -
验证输入合法性:对
quantity添加非负校验(如$quantity_to_add 则拒绝); -
HTML 层同步优化:Modal 表单中
<input type="number" min="0">已有基础限制,后端仍需二次校验。
✅ 总结
实现数据库字段的增量更新,应优先利用 SQL 原生算术能力(field = field + ?),配合预处理语句保障安全性。这不仅代码简洁、性能更优,还能规避并发写入风险。切勿依赖 PHP 层计算或字符串拼接 SQL——这是生产环境不可接受的安全红线。


















