PHP 简明教程

PHP MySQL 预处理语句

1. 预处理语句与绑定参数

预处理语句是一种用于高效且安全地多次执行相同(或相似)SQL 语句的特性。

预处理语句的工作原理如下:

  1. 预处理: SQL 语句模板被创建并发送到数据库。某些值留空,用问号 "?" 标记(例如:INSERT INTO MyGuests VALUES(?, ?, ?))。
  2. 数据库解析: 数据库解析、编译并对 SQL 语句模板进行查询优化,并将结果存储而不执行。
  3. 执行: 随后,应用程序将值绑定到参数上,数据库执行该语句。应用程序可以多次执行该语句,并传入不同的值。

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;
?>