使用 PDO 预处理语句进行多条件查询可以有效防止 SQL 注入,同时保持代码的清晰性和安全性。

基础方法:使用命名参数

$pdo = new PDO("mysql:host=localhost;dbname=test", "username", "password");

$name = 'John';
$age = 25;
$status = 'active';

$sql = "SELECT * FROM users WHERE name = :name AND age > :age AND status = :status";
$stmt = $pdo->prepare($sql);

$stmt->bindParam(':name', $name, PDO::PARAM_STR);
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
$stmt->bindParam(':status', $status, PDO::PARAM_STR);

$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

简化方法:使用 execute() 传递参数数组

$conditions = [
    'name' => 'John',
    'age' => 25,
    'status' => 'active'
];

$sql = "SELECT * FROM users WHERE name = :name AND age > :age AND status = :status";
$stmt = $pdo->prepare($sql);
$stmt->execute($conditions);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

动态构建多条件查询

$conditions = [];
$params = [];

// 根据用户输入动态添加条件
if (!empty($_GET['name'])) {
    $conditions[] = "name LIKE :name";
    $params[':name'] = '%' . $_GET['name'] . '%';
}

if (!empty($_GET['min_age'])) {
    $conditions[] = "age >= :min_age";
    $params[':min_age'] = $_GET['min_age'];
}

if (!empty($_GET['status'])) {
    $conditions[] = "status = :status";
    $params[':status'] = $_GET['status'];
}

$sql = "SELECT * FROM users";
if (!empty($conditions)) {
    $sql .= " WHERE " . implode(" AND ", $conditions);
}

$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

使用问号占位符

$name = 'John';
$age = 25;

$sql = "SELECT * FROM users WHERE name = ? AND age > ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $age]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

处理 IN 条件

$ids = [1, 3, 5, 7];
$placeholders = rtrim(str_repeat('?,', count($ids)), ',');

$sql = "SELECT * FROM products WHERE id IN ($placeholders)";
$stmt = $pdo->prepare($sql);
$stmt->execute($ids);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

总结

  1. 始终使用预处理语句防止 SQL 注入

  2. 对于动态查询,确保正确处理条件连接(AND/OR)

  3. 使用 bindValue() 而不是 bindParam() 除非你需要引用传递

  4. 考虑使用 PDO 的 setAttribute(PDO::ATTR_EMULATE_PREPARES, false) 以获得真正的预处理语句

实例:

try {
    $pdo = new PDO("mysql:host=localhost;dbname=test", "username", "password");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    // 收集查询参数
    $searchParams = [
        'name' => $_GET['name'] ?? null,
        'min_age' => $_GET['min_age'] ?? null,
        'max_age' => $_GET['max_age'] ?? null,
        'status' => $_GET['status'] ?? 'active'
    ];
    
    // 构建查询
    $conditions = [];
    $params = [];
    
    if (!empty($searchParams['name'])) {
        $conditions[] = "name LIKE :name";
        $params[':name'] = '%' . $searchParams['name'] . '%';
    }
    
    if (!empty($searchParams['min_age'])) {
        $conditions[] = "age >= :min_age";
        $params[':min_age'] = $searchParams['min_age'];
    }
    
    if (!empty($searchParams['max_age'])) {
        $conditions[] = "age <= :max_age";
        $params[':max_age'] = $searchParams['max_age'];
    }
    
    $conditions[] = "status = :status";
    $params[':status'] = $searchParams['status'];
    
    $sql = "SELECT * FROM users";
    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(" AND ", $conditions);
    }
    
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    foreach ($users as $user) {
        // 处理结果
    }
    
} catch (PDOException $e) {
    error_log("Database error: " . $e->getMessage());
    // 适当的错误处理
}