PHP 怎么PHP 参数化查询

wen PHP项目 2

本文目录导读:

PHP 怎么PHP 参数化查询

  1. 使用 PDO(PHP Data Objects)【推荐】
  2. 使用 MySQLi(MySQL Improved Extension)
  3. PDO vs MySQLi 对比
  4. 最佳实践与注意事项
  5. 完整的 PDO 封装示例

在 PHP 中,参数化查询(Parameterized Query)是防止 SQL 注入的最佳实践,它通过将 SQL 语句与数据分离,让数据库引擎预编译 SQL 模板,然后安全地绑定参数值。

以下是 PHP 中实现参数化查询的三种主流方式:


使用 PDO(PHP Data Objects)【推荐】

PDO 提供了统一的接口来访问多种数据库。

基本示例(MySQL)

<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$username = 'root';
$password = '';
try {
    $pdo = new PDO($dsn, $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // 参数化查询:使用命名占位符
    $sql = "SELECT * FROM users WHERE email = :email AND status = :status";
    $stmt = $pdo->prepare($sql);
    // 绑定参数
    $stmt->bindParam(':email', $email);
    $stmt->bindParam(':status', $status);
    $email = 'user@example.com';
    $status = 'active';
    $stmt->execute();
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    var_dump($results);
} catch (PDOException $e) {
    echo "数据库错误: " . $e->getMessage();
}
?>

使用问号占位符(位置参数)

$sql = "SELECT * FROM users WHERE email = ? AND status = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute(['user@example.com', 'active']);
// 或者
$stmt->execute([$email, $status]);

插入数据示例

$sql = "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)";
$stmt = $pdo->prepare($sql);
$data = [
    ':name' => '张三',
    ':email' => 'zhangsan@example.com',
    ':age' => 25
];
$stmt->execute($data);
echo "插入成功,ID: " . $pdo->lastInsertId();

使用 MySQLi(MySQL Improved Extension)

MySQLi 专门用于 MySQL 数据库,支持面向对象和过程式两种风格。

面向对象风格

<?php
$mysqli = new mysqli('localhost', 'root', '', 'testdb');
if ($mysqli->connect_error) {
    die('连接失败: ' . $mysqli->connect_error);
}
// 准备 SQL 语句
$sql = "SELECT id, name, email FROM users WHERE email = ? AND status = ?";
$stmt = $mysqli->prepare($sql);
// 绑定参数(s=string, i=integer, d=double, b=blob)
$email = 'user@example.com';
$status = 'active';
$stmt->bind_param("ss", $email, $status);
// 执行查询
$stmt->execute();
// 绑定结果变量(可选)
$stmt->bind_result($id, $name, $email_result);
// 获取结果
while ($stmt->fetch()) {
    echo "ID: $id, Name: $name, Email: $email_result<br>";
}
// 关闭语句和连接
$stmt->close();
$mysqli->close();
?>

插入数据

$sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
$stmt = $mysqli->prepare($sql);
$name = '李四';
$email = 'lisi@example.com';
$age = 30;
$stmt->bind_param("ssi", $name, $email, $age);
if ($stmt->execute()) {
    echo "插入成功,ID: " . $stmt->insert_id;
} else {
    echo "错误: " . $stmt->error;
}
$stmt->close();

PDO vs MySQLi 对比

特性 PDO MySQLi
数据库支持 12+ 种数据库 仅 MySQL
命名占位符 支持 (name) 仅问号
面向对象 是(也支持过程式)
预处理语句
性能 类似 类似
推荐指数

最佳实践与注意事项

始终使用参数化查询

  • 正确$stmt->execute(['user@example.com'])
  • 错误$pdo->query("SELECT * FROM users WHERE email='$email'")

不要手动转义(已过时)

  • 不要使用 mysqli_real_escape_string(),参数化查询已经自动处理转义

处理批量插入(事务)

try {
    $pdo->beginTransaction();
    $sql = "INSERT INTO users (name, email) VALUES (:name, :email)";
    $stmt = $pdo->prepare($sql);
    foreach ($users as $user) {
        $stmt->execute([
            ':name' => $user['name'],
            ':email' => $user['email']
        ]);
    }
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    echo "失败: " . $e->getMessage();
}

使用 LIKE 查询(注意通配符处理)

$search = "%{$keyword}%"; // 通配符在参数中,SQL 模板不变
$sql = "SELECT * FROM products WHERE name LIKE :keyword";
$stmt = $pdo->prepare($sql);
$stmt->execute([':keyword' => $search]);

完整的 PDO 封装示例

<?php
class Database {
    private static $instance = null;
    private $pdo;
    private function __construct() {
        $config = [
            'host' => 'localhost',
            'dbname' => 'testdb',
            'charset' => 'utf8mb4',
            'username' => 'root',
            'password' => ''
        ];
        $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}";
        $this->pdo = new PDO($dsn, $config['username'], $config['password'], [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false, // 关闭模拟预处理,使用真实的预处理
        ]);
    }
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    public function query($sql, $params = []) {
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt;
    }
    public function fetchAll($sql, $params = []) {
        return $this->query($sql, $params)->fetchAll();
    }
    public function fetchOne($sql, $params = []) {
        return $this->query($sql, $params)->fetch();
    }
    public function insert($table, $data) {
        $columns = implode(', ', array_keys($data));
        $placeholders = ':' . implode(', :', array_keys($data));
        $sql = "INSERT INTO $table ($columns) VALUES ($placeholders)";
        $this->query($sql, $data);
        return $this->pdo->lastInsertId();
    }
    public function update($table, $data, $where, $whereParams = []) {
        $set = '';
        foreach ($data as $key => $value) {
            $set .= "$key = :$key, ";
        }
        $set = rtrim($set, ', ');
        $sql = "UPDATE $table SET $set WHERE $where";
        $params = array_merge($data, $whereParams);
        return $this->query($sql, $params)->rowCount();
    }
}
// 使用示例
$db = Database::getInstance();
$users = $db->fetchAll("SELECT * FROM users WHERE age > :age", [':age' => 18]);
$user = $db->fetchOne("SELECT * FROM users WHERE id = :id", [':id' => 1]);
$newId = $db->insert('users', ['name' => '王五', 'email' => 'wangwu@example.com']);
?>

方法 推荐场景
PDO 通用、跨数据库、现代 PHP 项目首选
MySQLi 仅使用 MySQL、已有遗留项目

核心原则:永远不要直接将用户输入拼接到 SQL 语句中,始终使用参数化查询,这不仅防止 SQL 注入,还能提高查询性能和代码可读性。

抱歉,评论功能暂时关闭!