MySQL 预处理语句
用于 MySQL 的 PHP Prepared Statements
Section titled “用于 MySQL 的 PHP Prepared Statements”Prepared statements 是数据库安全的一项关键功能,特别用于防止 SQL 注入 (SQL injection) 漏洞,并且通常能提升性能。
Prepared Statements 和绑定参数 (Bound Parameters)
Section titled “Prepared Statements 和绑定参数 (Bound Parameters)”Prepared statement 是一个预编译的 SQL 模板,可以使用不同的参数多次执行。过程通常涉及两个阶段:
相对于执行原始 SQL 字符串的优点:
- 安全性(主要优势): Prepared statements 从根本上防止 SQL 注入。由于 SQL 代码模板和数据值是分开发送的,即使数据包含恶意 SQL 语法,数据库引擎也不会将绑定的数据解释为 SQL 命令。
- 效率: 数据库只需解析、编译和优化 SQL 查询模板一次。后续使用不同参数的执行会更快,因为它们复用了预编译计划。
- 降低带宽: 对于重复执行,每次只需将(通常较小的)参数值发送到服务器,而非整个 SQL 查询字符串。
经验法则: 无论何时将用户输入或任何变量数据合并到 SQL 查询中,请始终使用 prepared statements。
MySQLi 中的 Prepared Statements
Section titled “MySQLi 中的 Prepared Statements”MySQLi 支持使用位置占位符 (?) 的 prepared statements。
<?php$servername = "localhost";$username = "your_username";$password = "your_password";$dbname = "myDB"; // Assumes myDB database exists
// Create connection$conn = new mysqli($servername, $username, $password, $dbname);
// Check connectionif ($conn->connect_error) { die("Connection failed: " . $conn->connect_error);}
// 1. Prepare the statement with placeholders (?)$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");if ($stmt === false) { die("Prepare failed: (" . $conn->errno . ") " . $conn->error);}
// 2. Bind parameters// "sss" means three string parameters. Use 'i' for integer, 'd' for double, 'b' for blob.// The variables are passed by reference.$stmt->bind_param("sss", $firstname, $lastname, $email);if ($stmt === false) { die("Bind failed: (" . $stmt->errno . ") " . $stmt->error);}
// 3. Set parameters and execute for the first record$firstname = "John";$lastname = "Doe";$email = "john.doe@example.com";if (!$stmt->execute()) { echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;}
// 3. Set parameters and execute for the second record$firstname = "Mary";$lastname = "Moe";$email = "mary.moe@example.com";if (!$stmt->execute()) { echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;}
// 3. Set parameters and execute for the third record$firstname = "Julie";$lastname = "Dooley";$email = "julie.dooley@example.com";if (!$stmt->execute()) { echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;}
echo "New records created successfully";
// 4. Close statement and connection$stmt->close();$conn->close();?>解释:
$conn->prepare(...):使用?占位符创建 prepared statement 模板。$stmt->bind_param("sss", ...):将 PHP 变量绑定到占位符。第一个参数 ("sss") 指定每个占位符的数据类型 (s=字符串, i=整数, d=双精度浮点数, b=二进制大对象)。类型字符的数量必须与占位符的数量匹配。$stmt->execute():使用绑定变量的当前值执行 prepared statement。$stmt->close():释放 statement 资源。
PDO 中的 Prepared Statements
Section titled “PDO 中的 Prepared Statements”PDO 同时支持位置占位符 (?) 和命名占位符 (:name)。命名占位符通常更易读,特别是对于参数较多的查询。
使用命名占位符的示例:
Section titled “使用命名占位符的示例:”<?php$servername = "localhost";$username = "your_username";$password = "your_password";$dbname = "myDBPDO"; // Assumes myDBPDO database exists
try { $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); // 设置 PDO 错误模式为异常,以便更好地处理错误 $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 1. Prepare the statement with named placeholders (:firstname, etc.) $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (:firstname, :lastname, :email)");
// 绑定参数(也可以直接在 execute 数组中完成) // $stmt->bindParam(':firstname', $firstname); // $stmt->bindParam(':lastname', $lastname); // $stmt->bindParam(':email', $email);
// 设置参数并执行第一条记录(使用 execute 数组) $firstname = "John"; $lastname = "Doe"; $email = "john.pdo@example.com"; $stmt->execute([':firstname' => $firstname, ':lastname' => $lastname, ':email' => $email]);
// 设置参数并执行第二条记录 $firstname = "Mary"; $lastname = "Moe"; $email = "mary.pdo@example.com"; $stmt->execute([':firstname' => $firstname, ':lastname' => $lastname, ':email' => $email]);
// 设置参数并执行第三条记录 $firstname = "Julie"; $lastname = "Dooley"; $email = "julie.pdo@example.com"; $stmt->execute([':firstname' => $firstname, ':lastname' => $lastname, ':email' => $email]);
echo "New records created successfully using PDO";
} catch(PDOException $e) { echo "Error: " . $e->getMessage();}
// 关闭连接(在 PDO 中是可选的,会自动发生)$conn = null;?>解释:
$conn->prepare(...):使用命名占位符(例如:firstname)创建 prepared statement 模板。$stmt->bindParam(':name', $variable):将 PHP 变量绑定到命名占位符。这是按引用绑定。$stmt->bindValue(':name', $value):将特定值绑定到命名占位符。这是按值绑定。$stmt->execute([...]):执行 prepared statement。你可以直接向execute()传递一个关联数组,其中的键与命名占位符匹配。这通常是最简洁的方法。- 错误处理:
try...catch块可优雅地处理潜在的PDOException错误。
无论是使用 MySQLi 还是 PDO,始终使用 prepared statements 是编写安全高效的 PHP 数据库交互代码的基础。