PHP MySQL 预处理语句
预处理语句(Prepared Statements)是 MySQL 提供的一种增强 SQL 查询的方式。它可以有效防止 SQL 注入,提高性能,并简化代码。预处理语句通过先编译 SQL 查询模板,并将数据作为参数传递给查询,避免了 SQL 查询字符串拼接。
1. 预处理语句概念
预处理语句包括两部分:
- 准备阶段:创建 SQL 查询模板。
- 执行阶段:将参数绑定到查询模板并执行查询。
这种机制避免了用户输入直接与 SQL 查询拼接,提高了安全性。预处理语句在 SQL 查询执行前进行编译,之后可以多次执行,只需提供不同的参数。
2. 使用 MySQLi 进行预处理语句
2.1 预处理语句流程(面向对象)
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "my_database";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 创建预处理语句
$sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
$stmt = $conn->prepare($sql);
// 绑定参数
$stmt->bind_param("sss", $username, $email, $password);
// 设置参数并执行
$username = "user1";
$email = "user1@example.com";
$password = "password123";
$stmt->execute();
echo "新记录插入成功";
// 关闭连接
$stmt->close();
$conn->close();
?>
📌 解析:
$conn->prepare($sql):准备 SQL 语句模板。$stmt->bind_param("sss", $username, $email, $password):将用户输入的变量绑定到 SQL 语句中的参数。"sss"表示参数类型为三个字符串(string)。execute():执行预处理语句。
2.2 预处理语句流程(过程化)
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "my_database";
// 创建连接
$conn = mysqli_connect($servername, $username, $password, $dbname);
// 检查连接
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}
// 创建预处理语句
$sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
$stmt = mysqli_prepare($conn, $sql);
// 绑定参数
mysqli_stmt_bind_param($stmt, "sss", $username, $email, $password);
// 设置参数并执行
$username = "user2";
$email = "user2@example.com";
$password = "password456";
mysqli_stmt_execute($stmt);
echo "新记录插入成功";
// 关闭连接
mysqli_stmt_close($stmt);
mysqli_close($conn);
?>
📌 解析:
mysqli_prepare($conn, $sql):准备 SQL 查询。mysqli_stmt_bind_param($stmt, "sss", $username, $email, $password):绑定参数,其中"sss"指定了三个字符串类型。mysqli_stmt_execute($stmt):执行预处理语句。
3. 预处理语句查询(SELECT)示例
3.1 使用 MySQLi 进行 SELECT 查询
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "my_database";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 创建预处理语句
$sql = "SELECT id, username, email FROM users WHERE username = ?";
$stmt = $conn->prepare($sql);
// 绑定参数
$stmt->bind_param("s", $username);
// 设置参数并执行
$username = "user1";
$stmt->execute();
// 绑定结果变量
$stmt->bind_result($id, $username, $email);
// 获取查询结果
while ($stmt->fetch()) {
echo "ID: $id, 用户名: $username, 邮箱: $email<br>";
}
// 关闭连接
$stmt->close();
$conn->close();
?>
📌 解析:
bind_param("s", $username):s表示该参数是字符串类型。bind_result($id, $username, $email):绑定查询结果列到变量$id、$username和$email。fetch():获取结果集并输出。
3.2 使用 PDO 进行 SELECT 查询
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "my_database";
try {
// 创建 PDO 连接
$pdo = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 创建预处理语句
$sql = "SELECT id, username, email FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);
// 绑定参数
$stmt->bindParam(':username', $username, PDO::PARAM_STR);
// 设置参数并执行
$username = "user1";
$stmt->execute();
// 获取查询结果
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "ID: " . $row['id'] . ", 用户名: " . $row['username'] . ", 邮箱: " . $row['email'] . "<br>";
}
} catch (PDOException $e) {
echo "错误: " . $e->getMessage();
}
// 关闭连接
$pdo = null;
?>
📌 解析:
bindParam(':username', $username, PDO::PARAM_STR):绑定参数,使用 命名占位符(:username)。fetch(PDO::FETCH_ASSOC):获取结果行,返回关联数组。
4. 预处理语句的优势
4.1 防止 SQL 注入
预处理语句的最大优势是 防止 SQL 注入。通过将用户输入作为参数传递,而非直接拼接在查询字符串中,避免了潜在的 SQL 注入攻击。
例如,以下恶意用户输入:
$username = "admin' OR 1=1 -- ";
如果没有预处理语句,攻击者可能通过 SQL 注入操控查询,而预处理语句会自动对参数进行转义,防止注入。
4.2 提高性能
如果我们需要执行相同的查询多次(但传递不同的参数),预处理语句可以将 SQL 查询编译一次,然后多次执行,避免了重复编译的性能开销。
4.3 简化代码
预处理语句使得查询模板和数据分离,代码更加清晰和可维护。特别是对于复杂的 SQL 查询,代码结构更加整洁。
5. 常见错误和解决方案
5.1 错误:参数绑定失败
错误信息可能是:
Warning: mysqli_stmt_bind_param() expects parameter 1 to be mysqli_stmt, boolean given
原因:未能成功准备 SQL 语句(prepare() 返回 false),通常是 SQL 语句中存在错误。
解决方案:
- 检查 SQL 语句是否正确。
- 使用
mysqli_error($conn)查看错误详情。
5.2 错误:缺少参数
如果没有绑定所有参数,可能会遇到:
Warning: mysqli_stmt_bind_param() expects at least 3 parameters, 2 given
解决方案:
- 确保在绑定参数时,传递正确数量的参数,并确保每个参数都与 SQL 语句中的占位符匹配。
总结
| 方法 | 连接类型 | 安全性 | 性能 |
|---|---|---|---|
| MySQLi 预处理语句 | 面向对象、过程化 | 高 | 高 |
| PDO 预处理语句 | 面向对象 | 高 | 高 |
预处理语句是处理 SQL 查询的最佳实践,它不仅增强了 安全性,还提升了性能和代码的可维护性。强烈推荐 在处理用户输入时总是使用预处理语句来防止 SQL 注入。
更多详细内容请关注其他相关文章!