写程序时,经常要往数据库里存数据,比如用户注册、订单提交。很多人习惯直接把变量拼接到 SQL 语句里,看着简单,其实隐患不小。比如用户名是 ' OR '1'='1,一拼接,可能就把整个表查出来了——这就是典型的 SQL 注入。
为什么需要参数绑定
参数绑定就是为了解决这个问题。它不让数据直接参与 SQL 拼接,而是用占位符先占着位置,等执行时再把真实值安全地填进去。数据库会自动处理特殊字符,避免被恶意利用。
比如你要查某个用户的订单:
$username = $_POST['username'];
$sql = "SELECT * FROM orders WHERE user = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(1, $username);
$stmt->execute();
这里的 ? 就是占位符,bindParam 把变量和位置对应起来。就算输入的是恶意字符串,也会被当成普通文本处理,不会破坏 SQL 结构。
不同绑定方式怎么选
常见有两种占位方式:问号占位和命名占位。问号按顺序绑定,适合简单语句。
$sql = "INSERT INTO users (name, email) VALUES (?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $email]);
命名占位用冒号开头的名称,可读性更好,参数多的时候不容易乱。
$sql = "INSERT INTO users (name, email) VALUES (:name, :email)";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->execute();
绑定时注意变量作用域
用 bindParam 时传的是变量引用,如果循环中绑定同一个变量,最后所有占位可能都指向最后一次的值。这时候应该用 bindValue 或直接在 execute 里传数组。
foreach ($users as $user) {
$stmt->bindValue(':name', $user['name']);
$stmt->bindValue(':email', $user['email']);
$stmt->execute();
}
这样每次都是独立赋值,不会互相干扰。
不只是防注入
参数绑定还能提升性能。数据库可以缓存预编译的执行计划,下次同样的结构直接复用,不用重新解析。尤其在高频操作时,响应快了不少。
另外,日期、数字这些类型传进去也不用手动加引号或转格式,驱动会自动处理,少写几行代码,还更安全。
实际开发中,像 Laravel、ThinkPHP 这些框架默认都支持参数绑定,只要别手写拼接,基本能避开大部分风险。关键是养成习惯,凡是用户输入,一律走绑定流程。