PHP MySQL 预处理语句
1. 预处理语句与绑定参数
预处理语句是一种用于高效且安全地多次执行相同(或相似)SQL 语句的特性。
预处理语句的工作原理如下:
- 预处理: SQL 语句模板被创建并发送到数据库。某些值留空,用问号 "?" 标记(例如:
INSERT INTO MyGuests VALUES(?, ?, ?))。 - 数据库解析: 数据库解析、编译并对 SQL 语句模板进行查询优化,并将结果存储而不执行。
- 执行: 随后,应用程序将值绑定到参数上,数据库执行该语句。应用程序可以多次执行该语句,并传入不同的值。
1.1 预处理语句的优势
与直接执行 SQL 语句相比,预处理语句具有两大主要优势:
- 减少解析时间: 预处理语句大大减少了分析时间,因为查询的准备工作只进行一次(尽管语句会被多次执行)。
- 防止 SQL 注入: 绑定参数通过协议发送,不需要对参数值进行转义。如果原始语句模板来自受信任的源,则 SQL 注入是不可能发生的。
2. MySQLi 面向对象方式
下面的示例演示了在 MySQLi 中如何使用预处理语句和绑定参数:
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 预处理及绑定
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// 设置参数并执行
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "新记录插入成功";
$stmt->close();
$conn->close();
?>2.1 MySQLi 参数绑定详述
在上述示例代码中,以下这行代码值得深入分析:$stmt->bind_param("sss", $firstname, $lastname, $email);
该函数将 PHP 变量绑定到 SQL 查询中的问号 (?) 占位符。"sss" 参数列出了变量的数据类型,它告诉数据库这些参数是字符串(string)。
参数类型可以是以下四种之一:
- i - integer(整型)
- d - double(双精度浮点型)
- s - string(字符串)
- b - BLOB(二进制大对象,发送时为数据包)
每个参数都必须指定类型,这有助于数据库降低 SQL 注入的风险。
3. MySQLi 过程化方式
在过程化编程风格中,使用 mysqli_stmt_prepare()、mysqli_stmt_bind_param() 和 mysqli_stmt_execute() 来实现相同的逻辑。
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// 创建连接
$conn = mysqli_connect($servername, $username, $password, $dbname);
// 检查连接
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}
// 预处理 SQL 模板
$sql = "INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)";
$stmt = mysqli_stmt_init($conn);
if (mysqli_stmt_prepare($stmt, $sql)) {
// 绑定参数
mysqli_stmt_bind_param($stmt, "sss", $firstname, $lastname, $email);
// 设置变量并执行
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
mysqli_stmt_execute($stmt);
echo "记录插入成功";
}
mysqli_stmt_close($stmt);
mysqli_close($conn);
?>4. PDO 中的预处理语句
PDO 支持命名参数(Named Parameters),这使得 SQL 语句更具可读性。
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// 设置 PDO 错误模式为异常
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 预处理 SQL 并绑定参数
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email)
VALUES (:firstname, :lastname, :email)");
$stmt->bindParam(':firstname', $firstname);
$stmt->bindParam(':lastname', $lastname);
$stmt->bindParam(':email', $email);
// 插入行
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
// 再次插入另一行
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
echo "新记录插入成功";
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
$conn = null;
?>