PDO预处理通过分离SQL结构与数据防止SQL注入,核心步骤为:连接数据库、prepare()定义带占位符的SQL、绑定参数(推荐命名占位符提升可读性)、execute()执行;建议配置PDO::ATTR_EMULATE_PREPARES=>false以启用真实预处理,结合异常模式、正确字符集和默认获取模式确保安全与性能。

PHP使用PDO预处理语句的核心,在于将SQL查询的结构与数据分离。你先定义好一个带有占位符的SQL模板,然后把实际的数据“绑定”到这些占位符上,最后执行。这种做法最直接的好处就是能有效防止SQL注入攻击,因为数据库在执行查询前,会把数据和指令区别对待,数据永远不会被当作可执行的SQL代码。
连接数据库是第一步,这通常通过
new PDO()
prepare()
?
:name
然后是绑定参数。你可以用
bindParam()
bindValue()
execute()
PDO::PARAM_INT
bindParam()
最后,调用
execute()
SELECT
fetch()
fetchAll()
PDO::ATTR_EMULATE_PREPARES => false
立即学习“PHP免费学习笔记(深入)”;
<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$user = 'root';
$password = 'your_password'; // 请替换为你的数据库密码
try {
$pdo = new PDO($dsn, $user, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 错误模式设置为抛出异常
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认获取关联数组
PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理,使用真实预处理
]);
echo "数据库连接成功!<br>";
// 示例1:插入数据 (使用命名占位符)
$name = "李四";
$email = "lisi@example.com";
$age = 28;
$sql_insert = "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)";
$stmt_insert = $pdo->prepare($sql_insert);
// 直接在execute中传入参数数组,这是我个人最常用的方式
$stmt_insert->execute([
':name' => $name,
':email' => $email,
':age' => $age,
]);
echo "数据插入成功!<br>";
// 示例2:查询数据 (使用问号占位符和bindParam)
$minAge = 25;
$maxAge = 35;
$sql_select = "SELECT id, name, email, age FROM users WHERE age BETWEEN ? AND ?";
$stmt_select = $pdo->prepare($sql_select);
$stmt_select->bindParam(1, $minAge, PDO::PARAM_INT); // 第一个问号,绑定为整数
$stmt_select->bindParam(2, $maxAge, PDO::PARAM_INT); // 第二个问号,绑定为整数
$stmt_select->execute();
echo "查询年龄在 {$minAge} 到 {$maxAge} 之间的用户:<br>";
if ($stmt_select->rowCount() > 0) {
while ($row = $stmt_select->fetch()) {
echo "ID: " . $row['id'] . ", Name: " . $row['name'] . ", Email: " . $row['email'] . ", Age: " . $row['age'] . "<br>";
}
} else {
echo "没有找到符合条件的用户。<br>";
}
// 示例3:更新数据 (使用命名占位符和bindValue)
$newEmail = "zhangsan_new@example.com";
$userId = 1; // 假设要更新ID为1的用户
$sql_update = "UPDATE users SET email = :new_email WHERE id = :user_id";
$stmt_update = $pdo->prepare($sql_update);
$stmt_update->bindValue(':new_email', $newEmail); // 绑定值
$stmt_update->bindValue(':user_id', $userId, PDO::PARAM_INT); // 绑定值并指定类型
$stmt_update->execute();
echo "用户ID {$userId} 的邮箱已更新为 {$newEmail}。<br>";
} catch (PDOException $e) {
echo "数据库操作失败: " . $e->getMessage();
// 在生产环境中,更应该记录错误日志而不是直接输出给用户
}
?>我见过太多初学者,甚至是一些有经验的开发者,以为只要过滤了特殊字符、使用了
addslashes()
当你调用
$pdo->prepare($sql)
接着,当你通过
execute()
' OR '1'='1
举个例子,如果不用预处理,你可能会写成这样:
$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . $_POST['password'] . "'";
$_POST['username']
admin' OR '1'='1
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '...'
而使用预处理,即使
$_POST['username']
admin' OR '1'='1
SELECT * FROM users WHERE username = 'admin\' OR \'1\'=\'1' AND password = '...'
PDO::ATTR_EMULATE_PREPARES => false
在PDO预处理中,你通常会看到两种占位符:问号占位符(
?
:name
问号占位符,顾名思义,就是用一个问号来表示一个将来要绑定的值。它的优点是简洁,特别适合那些参数较少、顺序固定的简单查询。比如
SELECT * FROM users WHERE id = ? AND status = ?
命名占位符,则是用一个冒号后面跟着一个有意义的名称来表示占位符,比如
:username
:username
:age
SELECT * FROM products WHERE category = :cat_id AND price > :min_price AND price < :max_price AND seller_id IN (SELECT id FROM sellers WHERE category = :cat_id)
cat_id
所以,我的建议是:尽可能优先使用命名占位符。 它能让你的SQL语句更清晰、更易读、更不容易出错,尤其是在团队协作和长期维护的项目中,这一点至关重要。问号占位符可以作为一种备选,但仅限于那些非常简单、参数极少的查询。
很多人可能觉得,只要用了预处理就万事大吉了,但实际上,PDO的连接配置本身就藏着不少学问,它们不仅影响性能,更是安全防护的重要组成部分。
首先是错误处理模式(PDO::ATTR_ERRMODE
PDO::ERRMODE_EXCEPTION
PDOException
其次是字符集(charset=utf8mb4
utf8mb4
再来是禁用模拟预处理(PDO::ATTR_EMULATE_PREPARES => false
true
false
还有默认获取模式(PDO::ATTR_DEFAULT_FETCH_MODE
PDO::FETCH_ASSOC
fetch()
fetchAll()
PDO::FETCH_OBJ
fetch()
最后,关于持久连接(PDO::ATTR_PERSISTENT => true
以上就是php如何使用预处理语句?php PDO预处理语句防止SQL注入的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号