10.1 数据库基础与PDO简介

10.1.1 数据库连接基础

<?php
// 数据库连接基础
echo "=== 数据库连接基础 ===\n";

echo "--- PDO简介 ---\n";
echo "PDO (PHP Data Objects) 是PHP的数据库抽象层\n";
echo "支持多种数据库:MySQL、PostgreSQL、SQLite、Oracle等\n";
echo "提供统一的接口和预处理语句支持\n";

// 数据库配置
$dbConfig = [
    'mysql' => [
        'dsn' => 'mysql:host=localhost;dbname=test;charset=utf8mb4',
        'username' => 'root',
        'password' => '',
        'options' => [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
        ]
    ],
    'sqlite' => [
        'dsn' => 'sqlite:' . __DIR__ . '/test.db',
        'username' => null,
        'password' => null,
        'options' => [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    ]
];

echo "\n--- 基础连接示例 ---\n";

// 使用SQLite进行演示(无需额外配置)
try {
    $config = $dbConfig['sqlite'];
    $pdo = new PDO(
        $config['dsn'],
        $config['username'],
        $config['password'],
        $config['options']
    );
    
    echo "✓ 数据库连接成功\n";
    echo "数据库版本: " . $pdo->getAttribute(PDO::ATTR_SERVER_VERSION) . "\n";
    echo "驱动名称: " . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . "\n";
    
} catch (PDOException $e) {
    echo "✗ 数据库连接失败: " . $e->getMessage() . "\n";
    exit(1);
}

echo "\n--- 数据库连接管理类 ---\n";

// 数据库连接管理类
class DatabaseManager {
    private static $instances = [];
    private $pdo;
    private $config;
    
    private function __construct($config) {
        $this->config = $config;
        $this->connect();
    }
    
    public static function getInstance($name = 'default', $config = null) {
        if (!isset(self::$instances[$name])) {
            if ($config === null) {
                throw new InvalidArgumentException("首次获取实例时必须提供配置");
            }
            self::$instances[$name] = new self($config);
        }
        return self::$instances[$name];
    }
    
    private function connect() {
        try {
            $this->pdo = new PDO(
                $this->config['dsn'],
                $this->config['username'] ?? null,
                $this->config['password'] ?? null,
                $this->config['options'] ?? []
            );
        } catch (PDOException $e) {
            throw new RuntimeException("数据库连接失败: " . $e->getMessage());
        }
    }
    
    public function getPdo() {
        // 检查连接是否仍然有效
        try {
            $this->pdo->query('SELECT 1');
        } catch (PDOException $e) {
            // 重新连接
            $this->connect();
        }
        
        return $this->pdo;
    }
    
    public function query($sql, $params = []) {
        $stmt = $this->getPdo()->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 execute($sql, $params = []) {
        return $this->query($sql, $params)->rowCount();
    }
    
    public function lastInsertId() {
        return $this->getPdo()->lastInsertId();
    }
    
    public function beginTransaction() {
        return $this->getPdo()->beginTransaction();
    }
    
    public function commit() {
        return $this->getPdo()->commit();
    }
    
    public function rollback() {
        return $this->getPdo()->rollback();
    }
    
    public function inTransaction() {
        return $this->getPdo()->inTransaction();
    }
}

// 测试数据库管理器
$db = DatabaseManager::getInstance('main', $dbConfig['sqlite']);
echo "✓ 数据库管理器初始化成功\n";

// 创建测试表
$createTableSQL = "
CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)";

$db->execute($createTableSQL);
echo "✓ 用户表创建成功\n";

$createPostsTableSQL = "
CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    status VARCHAR(20) DEFAULT 'draft',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
)";

$db->execute($createPostsTableSQL);
echo "✓ 文章表创建成功\n";
?>

10.3 事务处理

10.3.1 事务基础

<?php
// 事务处理
echo "=== 事务处理 ===\n";

echo "--- 基础事务操作 ---\n";

// 基础事务示例
function transferMoney($db, $fromUserId, $toUserId, $amount) {
    $db->beginTransaction();
    
    try {
        // 检查发送方余额(假设用户表有balance字段)
        $sender = $db->fetchOne("SELECT id, username, balance FROM users WHERE id = ?", [$fromUserId]);
        if (!$sender) {
            throw new Exception("发送方用户不存在");
        }
        
        if ($sender['balance'] < $amount) {
            throw new Exception("余额不足");
        }
        
        // 检查接收方是否存在
        $receiver = $db->fetchOne("SELECT id, username FROM users WHERE id = ?", [$toUserId]);
        if (!$receiver) {
            throw new Exception("接收方用户不存在");
        }
        
        // 扣除发送方余额
        $result1 = $db->execute(
            "UPDATE users SET balance = balance - ? WHERE id = ?",
            [$amount, $fromUserId]
        );
        
        // 增加接收方余额
        $result2 = $db->execute(
            "UPDATE users SET balance = balance + ? WHERE id = ?",
            [$amount, $toUserId]
        );
        
        if ($result1 === 0 || $result2 === 0) {
            throw new Exception("转账操作失败");
        }
        
        // 记录转账日志
        $db->execute(
            "INSERT INTO transfer_logs (from_user_id, to_user_id, amount, created_at) VALUES (?, ?, ?, ?)",
            [$fromUserId, $toUserId, $amount, date('Y-m-d H:i:s')]
        );
        
        $db->commit();
        
        return [
            'success' => true,
            'message' => "转账成功:{$sender['username']} -> {$receiver['username']},金额:{$amount}"
        ];
        
    } catch (Exception $e) {
        $db->rollback();
        
        return [
            'success' => false,
            'message' => "转账失败:" . $e->getMessage()
        ];
    }
}

// 为演示添加balance字段和转账日志表
try {
    $db->execute("ALTER TABLE users ADD COLUMN balance DECIMAL(10,2) DEFAULT 0.00");
    echo "✓ 为用户表添加余额字段\n";
} catch (PDOException $e) {
    echo "余额字段可能已存在\n";
}

try {
    $createTransferLogsSQL = "
    CREATE TABLE IF NOT EXISTS transfer_logs (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        from_user_id INTEGER NOT NULL,
        to_user_id INTEGER NOT NULL,
        amount DECIMAL(10,2) NOT NULL,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (from_user_id) REFERENCES users(id),
        FOREIGN KEY (to_user_id) REFERENCES users(id)
    )";
    
    $db->execute($createTransferLogsSQL);
    echo "✓ 转账日志表创建成功\n";
} catch (PDOException $e) {
    echo "转账日志表可能已存在\n";
}

// 为用户设置初始余额
$db->execute("UPDATE users SET balance = 1000.00 WHERE balance = 0");

// 测试转账
$users = $db->fetchAll("SELECT id, username, balance FROM users LIMIT 2");
if (count($users) >= 2) {
    $result = transferMoney($db, $users[0]['id'], $users[1]['id'], 100.50);
    echo $result['message'] . "\n";
    
    // 查看转账后的余额
    $updatedUsers = $db->fetchAll(
        "SELECT username, balance FROM users WHERE id IN (?, ?)",
        [$users[0]['id'], $users[1]['id']]
    );
    
    foreach ($updatedUsers as $user) {
        echo "  {$user['username']}: {$user['balance']}\n";
    }
}

echo "\n--- 事务管理器 ---\n";

// 事务管理器类
class TransactionManager {
    private $db;
    private $transactionLevel = 0;
    private $savepoints = [];
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function transaction(callable $callback) {
        $this->begin();
        
        try {
            $result = $callback($this->db);
            $this->commit();
            return $result;
        } catch (Exception $e) {
            $this->rollback();
            throw $e;
        }
    }
    
    public function begin() {
        if ($this->transactionLevel === 0) {
            $this->db->beginTransaction();
        } else {
            // 嵌套事务使用保存点
            $savepoint = 'sp_' . $this->transactionLevel;
            $this->db->getPdo()->exec("SAVEPOINT $savepoint");
            $this->savepoints[] = $savepoint;
        }
        
        $this->transactionLevel++;
    }
    
    public function commit() {
        if ($this->transactionLevel === 0) {
            throw new RuntimeException('没有活动的事务');
        }
        
        $this->transactionLevel--;
        
        if ($this->transactionLevel === 0) {
            $this->db->commit();
        } else {
            // 释放保存点
            $savepoint = array_pop($this->savepoints);
            $this->db->getPdo()->exec("RELEASE SAVEPOINT $savepoint");
        }
    }
    
    public function rollback() {
        if ($this->transactionLevel === 0) {
            throw new RuntimeException('没有活动的事务');
        }
        
        $this->transactionLevel--;
        
        if ($this->transactionLevel === 0) {
            $this->db->rollback();
        } else {
            // 回滚到保存点
            $savepoint = array_pop($this->savepoints);
            $this->db->getPdo()->exec("ROLLBACK TO SAVEPOINT $savepoint");
        }
    }
    
    public function getTransactionLevel() {
        return $this->transactionLevel;
    }
    
    public function inTransaction() {
        return $this->transactionLevel > 0;
    }
}

$transactionManager = new TransactionManager($db);

// 测试事务管理器
$result = $transactionManager->transaction(function($db) {
    // 创建新用户
    $db->execute(
        "INSERT INTO users (username, email, password, balance) VALUES (?, ?, ?, ?)",
        ['transaction_test', 'transaction@example.com', password_hash('123456', PASSWORD_DEFAULT), 500.00]
    );
    
    $newUserId = $db->lastInsertId();
    
    // 创建用户的第一篇文章
    $db->execute(
        "INSERT INTO posts (user_id, title, content, status) VALUES (?, ?, ?, ?)",
        [$newUserId, '事务测试文章', '这是在事务中创建的文章', 'published']
    );
    
    return $newUserId;
});

echo "✓ 事务管理器测试成功,新用户ID: $result\n";

echo "\n--- 分布式事务模拟 ---\n";

// 分布式事务协调器(简化版)
class DistributedTransactionCoordinator {
    private $participants = [];
    
    public function addParticipant($name, $db) {
        $this->participants[$name] = [
            'db' => $db,
            'prepared' => false,
            'committed' => false
        ];
    }
    
    public function executeDistributedTransaction(array $operations) {
        // Phase 1: Prepare
        echo "阶段1:准备阶段\n";
        
        foreach ($this->participants as $name => &$participant) {
            try {
                $participant['db']->beginTransaction();
                
                // 执行该参与者的操作
                if (isset($operations[$name])) {
                    $operations[$name]($participant['db']);
                }
                
                $participant['prepared'] = true;
                echo "  ✓ 参与者 $name 准备完成\n";
                
            } catch (Exception $e) {
                echo "  ✗ 参与者 $name 准备失败: " . $e->getMessage() . "\n";
                $this->rollbackAll();
                return false;
            }
        }
        
        // Phase 2: Commit
        echo "阶段2:提交阶段\n";
        
        foreach ($this->participants as $name => &$participant) {
            try {
                $participant['db']->commit();
                $participant['committed'] = true;
                echo "  ✓ 参与者 $name 提交完成\n";
                
            } catch (Exception $e) {
                echo "  ✗ 参与者 $name 提交失败: " . $e->getMessage() . "\n";
                // 在实际分布式系统中,这里需要更复杂的恢复机制
                return false;
            }
        }
        
        return true;
    }
    
    private function rollbackAll() {
        echo "回滚所有参与者\n";
        
        foreach ($this->participants as $name => &$participant) {
            if ($participant['prepared'] && !$participant['committed']) {
                try {
                    $participant['db']->rollback();
                    echo "  ✓ 参与者 $name 回滚完成\n";
                } catch (Exception $e) {
                    echo "  ✗ 参与者 $name 回滚失败: " . $e->getMessage() . "\n";
                }
            }
        }
    }
}

// 模拟分布式事务(使用同一个数据库的不同连接)
$coordinator = new DistributedTransactionCoordinator();
$coordinator->addParticipant('db1', $db);
$coordinator->addParticipant('db2', DatabaseManager::getInstance('secondary', $dbConfig['sqlite']));

$operations = [
    'db1' => function($db) {
        $db->execute(
            "INSERT INTO users (username, email, password, balance) VALUES (?, ?, ?, ?)",
            ['distributed_user1', 'dist1@example.com', password_hash('123456', PASSWORD_DEFAULT), 1000.00]
        );
    },
    'db2' => function($db) {
        $db->execute(
            "INSERT INTO users (username, email, password, balance) VALUES (?, ?, ?, ?)",
            ['distributed_user2', 'dist2@example.com', password_hash('123456', PASSWORD_DEFAULT), 1000.00]
        );
    }
];

$success = $coordinator->executeDistributedTransaction($operations);
echo $success ? "✓ 分布式事务执行成功\n" : "✗ 分布式事务执行失败\n";
?>

10.3.2 事务隔离级别

<?php
// 事务隔离级别
echo "=== 事务隔离级别 ===\n";

// 隔离级别管理器
class IsolationLevelManager {
    private $db;
    
    const READ_UNCOMMITTED = 'READ UNCOMMITTED';
    const READ_COMMITTED = 'READ COMMITTED';
    const REPEATABLE_READ = 'REPEATABLE READ';
    const SERIALIZABLE = 'SERIALIZABLE';
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function setIsolationLevel($level) {
        // 注意:SQLite不支持所有隔离级别,这里仅作演示
        try {
            $this->db->getPdo()->exec("PRAGMA read_uncommitted = " . ($level === self::READ_UNCOMMITTED ? '1' : '0'));
            echo "✓ 设置隔离级别为: $level\n";
        } catch (PDOException $e) {
            echo "✗ 设置隔离级别失败: " . $e->getMessage() . "\n";
        }
    }
    
    public function getCurrentIsolationLevel() {
        try {
            $result = $this->db->fetchOne("PRAGMA read_uncommitted");
            return $result ? self::READ_UNCOMMITTED : self::READ_COMMITTED;
        } catch (PDOException $e) {
            return 'UNKNOWN';
        }
    }
    
    public function demonstratePhantomRead() {
        echo "\n--- 幻读演示 ---\n";
        
        // 创建两个连接模拟并发
        $conn1 = DatabaseManager::getInstance('conn1', $dbConfig['sqlite']);
        $conn2 = DatabaseManager::getInstance('conn2', $dbConfig['sqlite']);
        
        echo "连接1开始事务并查询用户数量\n";
        $conn1->beginTransaction();
        $count1 = $conn1->fetchOne("SELECT COUNT(*) as count FROM users")['count'];
        echo "连接1查询到用户数量: $count1\n";
        
        echo "\n连接2插入新用户\n";
        $conn2->execute(
            "INSERT INTO users (username, email, password) VALUES (?, ?, ?)",
            ['phantom_user', 'phantom@example.com', password_hash('123456', PASSWORD_DEFAULT)]
        );
        echo "连接2插入用户成功\n";
        
        echo "\n连接1再次查询用户数量\n";
        $count2 = $conn1->fetchOne("SELECT COUNT(*) as count FROM users")['count'];
        echo "连接1再次查询到用户数量: $count2\n";
        
        if ($count2 > $count1) {
            echo "✓ 发生了幻读现象\n";
        } else {
            echo "✗ 未发生幻读现象\n";
        }
        
        $conn1->rollback();
    }
    
    public function demonstrateDirtyRead() {
        echo "\n--- 脏读演示 ---\n";
        
        $conn1 = DatabaseManager::getInstance('conn1', $dbConfig['sqlite']);
        $conn2 = DatabaseManager::getInstance('conn2', $dbConfig['sqlite']);
        
        // 获取一个测试用户
        $user = $conn1->fetchOne("SELECT * FROM users WHERE balance > 0 LIMIT 1");
        if (!$user) {
            echo "没有可用的测试用户\n";
            return;
        }
        
        $originalBalance = $user['balance'];
        echo "用户 {$user['username']} 原始余额: $originalBalance\n";
        
        echo "\n连接1开始事务并修改余额\n";
        $conn1->beginTransaction();
        $conn1->execute(
            "UPDATE users SET balance = balance + 500 WHERE id = ?",
            [$user['id']]
        );
        echo "连接1修改余额 +500(未提交)\n";
        
        echo "\n连接2读取余额\n";
        $modifiedUser = $conn2->fetchOne("SELECT balance FROM users WHERE id = ?", [$user['id']]);
        echo "连接2读取到余额: {$modifiedUser['balance']}\n";
        
        if ($modifiedUser['balance'] != $originalBalance) {
            echo "✓ 发生了脏读现象\n";
        } else {
            echo "✗ 未发生脏读现象\n";
        }
        
        echo "\n连接1回滚事务\n";
        $conn1->rollback();
        
        $finalUser = $conn2->fetchOne("SELECT balance FROM users WHERE id = ?", [$user['id']]);
        echo "最终余额: {$finalUser['balance']}\n";
    }
}

$isolationManager = new IsolationLevelManager($db);

echo "当前隔离级别: " . $isolationManager->getCurrentIsolationLevel() . "\n";

// 演示并发问题
$isolationManager->demonstratePhantomRead();
$isolationManager->demonstrateDirtyRead();
?>

10.4 预处理语句与安全

10.4.1 预处理语句详解

<?php
// 预处理语句与安全
echo "=== 预处理语句与安全 ===\n";

echo "--- 预处理语句基础 ---\n";

// 预处理语句管理器
class PreparedStatementManager {
    private $db;
    private $statements = [];
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function prepare($name, $sql) {
        $this->statements[$name] = $this->db->getPdo()->prepare($sql);
        return $this->statements[$name];
    }
    
    public function execute($name, $params = []) {
        if (!isset($this->statements[$name])) {
            throw new InvalidArgumentException("预处理语句 '$name' 不存在");
        }
        
        $stmt = $this->statements[$name];
        $stmt->execute($params);
        return $stmt;
    }
    
    public function fetchAll($name, $params = []) {
        return $this->execute($name, $params)->fetchAll();
    }
    
    public function fetchOne($name, $params = []) {
        return $this->execute($name, $params)->fetch();
    }
    
    public function getStatement($name) {
        return $this->statements[$name] ?? null;
    }
    
    public function closeStatement($name) {
        if (isset($this->statements[$name])) {
            $this->statements[$name]->closeCursor();
            unset($this->statements[$name]);
        }
    }
    
    public function closeAllStatements() {
        foreach ($this->statements as $name => $stmt) {
            $stmt->closeCursor();
        }
        $this->statements = [];
    }
}

$stmtManager = new PreparedStatementManager($db);

// 准备常用的预处理语句
$stmtManager->prepare('find_user_by_username', "SELECT * FROM users WHERE username = ?");
$stmtManager->prepare('find_user_by_email', "SELECT * FROM users WHERE email = ?");
$stmtManager->prepare('insert_user', "INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
$stmtManager->prepare('update_user_balance', "UPDATE users SET balance = ? WHERE id = ?");
$stmtManager->prepare('find_posts_by_user', "SELECT * FROM posts WHERE user_id = ? ORDER BY created_at DESC");

echo "✓ 预处理语句准备完成\n";

// 测试预处理语句
$user = $stmtManager->fetchOne('find_user_by_username', ['zhangsan']);
if ($user) {
    echo "找到用户: {$user['username']}\n";
    
    $posts = $stmtManager->fetchAll('find_posts_by_user', [$user['id']]);
    echo "该用户有 " . count($posts) . " 篇文章\n";
}

echo "\n--- SQL注入防护 ---\n";

// SQL注入防护演示
class SQLInjectionDemo {
    private $db;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function vulnerableQuery($username) {
        // 危险:直接拼接SQL
        $sql = "SELECT * FROM users WHERE username = '$username'";
        echo "危险SQL: $sql\n";
        
        try {
            $result = $this->db->getPdo()->query($sql);
            return $result ? $result->fetchAll() : [];
        } catch (PDOException $e) {
            echo "查询失败: " . $e->getMessage() . "\n";
            return [];
        }
    }
    
    public function safeQuery($username) {
        // 安全:使用预处理语句
        $sql = "SELECT * FROM users WHERE username = ?";
        echo "安全SQL: $sql\n";
        
        try {
            $stmt = $this->db->getPdo()->prepare($sql);
            $stmt->execute([$username]);
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            echo "查询失败: " . $e->getMessage() . "\n";
            return [];
        }
    }
    
    public function demonstrateInjection() {
        echo "\n=== SQL注入演示 ===\n";
        
        // 正常查询
        echo "--- 正常查询 ---\n";
        $normalInput = "zhangsan";
        echo "输入: $normalInput\n";
        
        $vulnerableResult = $this->vulnerableQuery($normalInput);
        echo "危险方法结果数量: " . count($vulnerableResult) . "\n";
        
        $safeResult = $this->safeQuery($normalInput);
        echo "安全方法结果数量: " . count($safeResult) . "\n";
        
        // 注入攻击尝试
        echo "\n--- 注入攻击尝试 ---\n";
        $maliciousInput = "zhangsan' OR '1'='1";
        echo "恶意输入: $maliciousInput\n";
        
        $vulnerableResult = $this->vulnerableQuery($maliciousInput);
        echo "危险方法结果数量: " . count($vulnerableResult) . "\n";
        
        $safeResult = $this->safeQuery($maliciousInput);
        echo "安全方法结果数量: " . count($safeResult) . "\n";
        
        if (count($vulnerableResult) > count($safeResult)) {
            echo "✗ 危险方法存在SQL注入漏洞\n";
        } else {
            echo "✓ 两种方法结果一致\n";
        }
    }
}

$injectionDemo = new SQLInjectionDemo($db);
$injectionDemo->demonstrateInjection();

echo "\n--- 输入验证与清理 ---\n";

// 输入验证器
class InputValidator {
    public static function validateEmail($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    public static function validateUsername($username) {
        // 用户名只能包含字母、数字、下划线,长度3-20
        return preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username);
    }
    
    public static function sanitizeString($input) {
        // 移除HTML标签和特殊字符
        $input = strip_tags($input);
        $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
        return trim($input);
    }
    
    public static function validateInteger($value, $min = null, $max = null) {
        $value = filter_var($value, FILTER_VALIDATE_INT);
        if ($value === false) {
            return false;
        }
        
        if ($min !== null && $value < $min) {
            return false;
        }
        
        if ($max !== null && $value > $max) {
            return false;
        }
        
        return $value;
    }
    
    public static function validateFloat($value, $min = null, $max = null) {
        $value = filter_var($value, FILTER_VALIDATE_FLOAT);
        if ($value === false) {
            return false;
        }
        
        if ($min !== null && $value < $min) {
            return false;
        }
        
        if ($max !== null && $value > $max) {
            return false;
        }
        
        return $value;
    }
}

// 安全的用户注册函数
function safeRegisterUser($db, $username, $email, $password) {
    // 输入验证
    if (!InputValidator::validateUsername($username)) {
        throw new InvalidArgumentException('用户名格式不正确');
    }
    
    if (!InputValidator::validateEmail($email)) {
        throw new InvalidArgumentException('邮箱格式不正确');
    }
    
    if (strlen($password) < 6) {
        throw new InvalidArgumentException('密码长度至少6位');
    }
    
    // 清理输入
    $username = InputValidator::sanitizeString($username);
    $email = InputValidator::sanitizeString($email);
    
    // 检查用户名和邮箱是否已存在
    $existingUser = $db->fetchOne(
        "SELECT id FROM users WHERE username = ? OR email = ?",
        [$username, $email]
    );
    
    if ($existingUser) {
        throw new RuntimeException('用户名或邮箱已存在');
    }
    
    // 密码哈希
    $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
    
    // 插入用户
    $result = $db->execute(
        "INSERT INTO users (username, email, password) VALUES (?, ?, ?)",
        [$username, $email, $hashedPassword]
    );
    
    if ($result > 0) {
        return $db->lastInsertId();
    } else {
        throw new RuntimeException('用户注册失败');
    }
}

// 测试安全注册
try {
    $newUserId = safeRegisterUser($db, 'safe_user', 'safe@example.com', 'securepassword123');
    echo "✓ 安全注册用户成功,ID: $newUserId\n";
} catch (Exception $e) {
    echo "✗ 注册失败: " . $e->getMessage() . "\n";
}

// 测试无效输入
try {
    safeRegisterUser($db, 'invalid user!', 'invalid-email', '123');
} catch (Exception $e) {
    echo "✓ 成功拦截无效输入: " . $e->getMessage() . "\n";
}
?>

10.2 基础CRUD操作

10.2.1 插入操作(Create)

<?php
// 插入操作
echo "=== 插入操作 ===\n";

echo "--- 基础插入 ---\n";

// 单条记录插入
$insertUserSQL = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";

$users = [
    ['zhangsan', 'zhangsan@example.com', password_hash('123456', PASSWORD_DEFAULT)],
    ['lisi', 'lisi@example.com', password_hash('123456', PASSWORD_DEFAULT)],
    ['wangwu', 'wangwu@example.com', password_hash('123456', PASSWORD_DEFAULT)]
];

foreach ($users as $user) {
    try {
        $result = $db->execute($insertUserSQL, $user);
        $userId = $db->lastInsertId();
        echo "✓ 用户 {$user[0]} 插入成功,ID: $userId\n";
    } catch (PDOException $e) {
        echo "✗ 用户 {$user[0]} 插入失败: " . $e->getMessage() . "\n";
    }
}

echo "\n--- 批量插入 ---\n";

// 批量插入类
class BatchInserter {
    private $db;
    private $table;
    private $columns;
    private $batchSize;
    private $buffer = [];
    
    public function __construct($db, $table, $columns, $batchSize = 100) {
        $this->db = $db;
        $this->table = $table;
        $this->columns = $columns;
        $this->batchSize = $batchSize;
    }
    
    public function add($data) {
        $this->buffer[] = $data;
        
        if (count($this->buffer) >= $this->batchSize) {
            $this->flush();
        }
    }
    
    public function flush() {
        if (empty($this->buffer)) {
            return 0;
        }
        
        $placeholders = '(' . str_repeat('?,', count($this->columns) - 1) . '?)';
        $values = str_repeat($placeholders . ',', count($this->buffer) - 1) . $placeholders;
        
        $sql = "INSERT INTO {$this->table} (" . implode(',', $this->columns) . ") VALUES $values";
        
        $params = [];
        foreach ($this->buffer as $row) {
            $params = array_merge($params, array_values($row));
        }
        
        $result = $this->db->execute($sql, $params);
        $count = count($this->buffer);
        $this->buffer = [];
        
        return $count;
    }
    
    public function __destruct() {
        $this->flush();
    }
}

// 测试批量插入
$batchInserter = new BatchInserter($db, 'posts', ['user_id', 'title', 'content', 'status']);

$posts = [
    [1, 'PHP基础教程', 'PHP是一种流行的服务器端脚本语言', 'published'],
    [1, 'MySQL数据库', '关系型数据库管理系统', 'published'],
    [2, 'JavaScript入门', '前端编程语言', 'draft'],
    [2, 'CSS样式设计', '网页样式表语言', 'published'],
    [3, 'Python编程', '简洁优雅的编程语言', 'draft']
];

foreach ($posts as $post) {
    $batchInserter->add($post);
}

$insertedCount = $batchInserter->flush();
echo "✓ 批量插入 $insertedCount 条文章记录\n";

echo "\n--- 插入或更新 ---\n";

// 插入或更新(UPSERT)
class UpsertHelper {
    private $db;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function upsertUser($username, $email, $password) {
        // SQLite使用INSERT OR REPLACE
        $sql = "
            INSERT OR REPLACE INTO users (id, username, email, password, created_at, updated_at)
            VALUES (
                (SELECT id FROM users WHERE username = ? OR email = ?),
                ?, ?, ?, 
                COALESCE((SELECT created_at FROM users WHERE username = ? OR email = ?), CURRENT_TIMESTAMP),
                CURRENT_TIMESTAMP
            )
        ";
        
        $params = [$username, $email, $username, $email, $password, $username, $email];
        return $this->db->execute($sql, $params);
    }
    
    public function upsertUserSimple($username, $email, $password) {
        // 先尝试更新
        $updateSQL = "UPDATE users SET email = ?, password = ?, updated_at = CURRENT_TIMESTAMP WHERE username = ?";
        $result = $this->db->execute($updateSQL, [$email, $password, $username]);
        
        if ($result === 0) {
            // 如果没有更新任何行,则插入新记录
            $insertSQL = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
            $this->db->execute($insertSQL, [$username, $email, $password]);
            return 'inserted';
        } else {
            return 'updated';
        }
    }
}

$upsertHelper = new UpsertHelper($db);

// 测试插入或更新
$result = $upsertHelper->upsertUserSimple('zhangsan', 'zhangsan_new@example.com', password_hash('newpassword', PASSWORD_DEFAULT));
echo "✓ 用户zhangsan操作结果: $result\n";

$result = $upsertHelper->upsertUserSimple('newuser', 'newuser@example.com', password_hash('password', PASSWORD_DEFAULT));
echo "✓ 用户newuser操作结果: $result\n";
?>

10.2.2 查询操作(Read)

<?php
// 查询操作
echo "=== 查询操作 ===\n";

echo "--- 基础查询 ---\n";

// 查询所有用户
$users = $db->fetchAll("SELECT * FROM users ORDER BY created_at");
echo "用户总数: " . count($users) . "\n";
foreach ($users as $user) {
    echo "  ID: {$user['id']}, 用户名: {$user['username']}, 邮箱: {$user['email']}\n";
}

echo "\n--- 条件查询 ---\n";

// 按条件查询
$user = $db->fetchOne("SELECT * FROM users WHERE username = ?", ['zhangsan']);
if ($user) {
    echo "找到用户: {$user['username']} ({$user['email']})\n";
} else {
    echo "用户不存在\n";
}

// 模糊查询
$users = $db->fetchAll("SELECT * FROM users WHERE email LIKE ?", ['%example.com']);
echo "\n使用example.com邮箱的用户数: " . count($users) . "\n";

echo "\n--- 查询构建器 ---\n";

// 查询构建器类
class QueryBuilder {
    private $db;
    private $table;
    private $select = ['*'];
    private $where = [];
    private $params = [];
    private $orderBy = [];
    private $groupBy = [];
    private $having = [];
    private $limit = null;
    private $offset = null;
    private $joins = [];
    
    public function __construct($db, $table) {
        $this->db = $db;
        $this->table = $table;
    }
    
    public function select($columns) {
        if (is_array($columns)) {
            $this->select = $columns;
        } else {
            $this->select = func_get_args();
        }
        return $this;
    }
    
    public function where($column, $operator, $value = null) {
        if ($value === null) {
            $value = $operator;
            $operator = '=';
        }
        
        $this->where[] = "$column $operator ?";
        $this->params[] = $value;
        return $this;
    }
    
    public function whereIn($column, $values) {
        $placeholders = str_repeat('?,', count($values) - 1) . '?';
        $this->where[] = "$column IN ($placeholders)";
        $this->params = array_merge($this->params, $values);
        return $this;
    }
    
    public function whereBetween($column, $min, $max) {
        $this->where[] = "$column BETWEEN ? AND ?";
        $this->params[] = $min;
        $this->params[] = $max;
        return $this;
    }
    
    public function whereNull($column) {
        $this->where[] = "$column IS NULL";
        return $this;
    }
    
    public function whereNotNull($column) {
        $this->where[] = "$column IS NOT NULL";
        return $this;
    }
    
    public function orderBy($column, $direction = 'ASC') {
        $this->orderBy[] = "$column $direction";
        return $this;
    }
    
    public function groupBy($column) {
        $this->groupBy[] = $column;
        return $this;
    }
    
    public function having($column, $operator, $value = null) {
        if ($value === null) {
            $value = $operator;
            $operator = '=';
        }
        
        $this->having[] = "$column $operator ?";
        $this->params[] = $value;
        return $this;
    }
    
    public function limit($limit, $offset = null) {
        $this->limit = $limit;
        if ($offset !== null) {
            $this->offset = $offset;
        }
        return $this;
    }
    
    public function offset($offset) {
        $this->offset = $offset;
        return $this;
    }
    
    public function join($table, $first, $operator, $second = null) {
        return $this->addJoin('INNER JOIN', $table, $first, $operator, $second);
    }
    
    public function leftJoin($table, $first, $operator, $second = null) {
        return $this->addJoin('LEFT JOIN', $table, $first, $operator, $second);
    }
    
    public function rightJoin($table, $first, $operator, $second = null) {
        return $this->addJoin('RIGHT JOIN', $table, $first, $operator, $second);
    }
    
    private function addJoin($type, $table, $first, $operator, $second) {
        if ($second === null) {
            $second = $operator;
            $operator = '=';
        }
        
        $this->joins[] = "$type $table ON $first $operator $second";
        return $this;
    }
    
    public function toSQL() {
        $sql = 'SELECT ' . implode(', ', $this->select) . ' FROM ' . $this->table;
        
        if (!empty($this->joins)) {
            $sql .= ' ' . implode(' ', $this->joins);
        }
        
        if (!empty($this->where)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->where);
        }
        
        if (!empty($this->groupBy)) {
            $sql .= ' GROUP BY ' . implode(', ', $this->groupBy);
        }
        
        if (!empty($this->having)) {
            $sql .= ' HAVING ' . implode(' AND ', $this->having);
        }
        
        if (!empty($this->orderBy)) {
            $sql .= ' ORDER BY ' . implode(', ', $this->orderBy);
        }
        
        if ($this->limit !== null) {
            $sql .= ' LIMIT ' . $this->limit;
            if ($this->offset !== null) {
                $sql .= ' OFFSET ' . $this->offset;
            }
        }
        
        return $sql;
    }
    
    public function get() {
        return $this->db->fetchAll($this->toSQL(), $this->params);
    }
    
    public function first() {
        $this->limit(1);
        return $this->db->fetchOne($this->toSQL(), $this->params);
    }
    
    public function count() {
        $originalSelect = $this->select;
        $this->select = ['COUNT(*) as count'];
        $result = $this->db->fetchOne($this->toSQL(), $this->params);
        $this->select = $originalSelect;
        return $result ? (int)$result['count'] : 0;
    }
    
    public function exists() {
        return $this->count() > 0;
    }
}

// 测试查询构建器
echo "--- 查询构建器测试 ---\n";

$query = new QueryBuilder($db, 'users');

// 简单查询
$users = $query->select('id', 'username', 'email')
               ->where('username', 'LIKE', '%san%')
               ->orderBy('created_at', 'DESC')
               ->get();

echo "包含'san'的用户:\n";
foreach ($users as $user) {
    echo "  {$user['username']} - {$user['email']}\n";
}

// 复杂查询
$query = new QueryBuilder($db, 'posts');
$posts = $query->select('posts.*', 'users.username')
               ->leftJoin('users', 'posts.user_id', 'users.id')
               ->where('posts.status', 'published')
               ->orderBy('posts.created_at', 'DESC')
               ->get();

echo "\n已发布的文章:\n";
foreach ($posts as $post) {
    echo "  《{$post['title']}》 - 作者: {$post['username']}\n";
}

// 统计查询
$query = new QueryBuilder($db, 'posts');
$count = $query->where('status', 'published')->count();
echo "\n已发布文章总数: $count\n";

// 分页查询
$query = new QueryBuilder($db, 'users');
$users = $query->select('username', 'email')
               ->orderBy('created_at')
               ->limit(2, 1) // 每页2条,跳过1条
               ->get();

echo "\n分页查询结果(第2页,每页2条):\n";
foreach ($users as $user) {
    echo "  {$user['username']} - {$user['email']}\n";
}
?>

10.2.3 更新操作(Update)

<?php
// 更新操作
echo "=== 更新操作 ===\n";

echo "--- 基础更新 ---\n";

// 单条记录更新
$updateSQL = "UPDATE users SET email = ?, updated_at = CURRENT_TIMESTAMP WHERE username = ?";
$result = $db->execute($updateSQL, ['zhangsan_updated@example.com', 'zhangsan']);
echo "更新用户zhangsan的邮箱,影响行数: $result\n";

// 批量更新
$updateSQL = "UPDATE posts SET status = ? WHERE user_id = ?";
$result = $db->execute($updateSQL, ['published', 2]);
echo "更新用户ID为2的所有文章状态,影响行数: $result\n";

echo "\n--- 条件更新 ---\n";

// 更新构建器类
class UpdateBuilder {
    private $db;
    private $table;
    private $set = [];
    private $where = [];
    private $params = [];
    
    public function __construct($db, $table) {
        $this->db = $db;
        $this->table = $table;
    }
    
    public function set($column, $value) {
        $this->set[] = "$column = ?";
        $this->params[] = $value;
        return $this;
    }
    
    public function increment($column, $amount = 1) {
        $this->set[] = "$column = $column + ?";
        $this->params[] = $amount;
        return $this;
    }
    
    public function decrement($column, $amount = 1) {
        $this->set[] = "$column = $column - ?";
        $this->params[] = $amount;
        return $this;
    }
    
    public function where($column, $operator, $value = null) {
        if ($value === null) {
            $value = $operator;
            $operator = '=';
        }
        
        $this->where[] = "$column $operator ?";
        $this->params[] = $value;
        return $this;
    }
    
    public function whereIn($column, $values) {
        $placeholders = str_repeat('?,', count($values) - 1) . '?';
        $this->where[] = "$column IN ($placeholders)";
        $this->params = array_merge($this->params, $values);
        return $this;
    }
    
    public function toSQL() {
        if (empty($this->set)) {
            throw new InvalidArgumentException('没有设置要更新的字段');
        }
        
        $sql = 'UPDATE ' . $this->table . ' SET ' . implode(', ', $this->set);
        
        if (!empty($this->where)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->where);
        }
        
        return $sql;
    }
    
    public function execute() {
        return $this->db->execute($this->toSQL(), $this->params);
    }
}

// 测试更新构建器
$updateBuilder = new UpdateBuilder($db, 'users');
$result = $updateBuilder->set('email', 'lisi_new@example.com')
                        ->set('updated_at', date('Y-m-d H:i:s'))
                        ->where('username', 'lisi')
                        ->execute();

echo "使用更新构建器更新用户lisi,影响行数: $result\n";

echo "\n--- 批量更新不同值 ---\n";

// 批量更新不同值的辅助类
class BatchUpdater {
    private $db;
    private $table;
    private $keyColumn;
    
    public function __construct($db, $table, $keyColumn = 'id') {
        $this->db = $db;
        $this->table = $table;
        $this->keyColumn = $keyColumn;
    }
    
    public function updateMultiple($updates) {
        if (empty($updates)) {
            return 0;
        }
        
        $this->db->beginTransaction();
        
        try {
            $totalAffected = 0;
            
            foreach ($updates as $keyValue => $data) {
                $setParts = [];
                $params = [];
                
                foreach ($data as $column => $value) {
                    $setParts[] = "$column = ?";
                    $params[] = $value;
                }
                
                $params[] = $keyValue;
                
                $sql = "UPDATE {$this->table} SET " . implode(', ', $setParts) . 
                       " WHERE {$this->keyColumn} = ?";
                
                $affected = $this->db->execute($sql, $params);
                $totalAffected += $affected;
            }
            
            $this->db->commit();
            return $totalAffected;
            
        } catch (Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }
    
    public function updateBatch($data, $columns) {
        if (empty($data) || empty($columns)) {
            return 0;
        }
        
        // 构建CASE WHEN语句
        $cases = [];
        $keyValues = [];
        $allParams = [];
        
        foreach ($columns as $column) {
            $whenClauses = [];
            foreach ($data as $row) {
                $whenClauses[] = "WHEN ? THEN ?";
                $allParams[] = $row[$this->keyColumn];
                $allParams[] = $row[$column];
            }
            $cases[] = "$column = CASE {$this->keyColumn} " . implode(' ', $whenClauses) . " ELSE $column END";
        }
        
        foreach ($data as $row) {
            $keyValues[] = $row[$this->keyColumn];
        }
        
        $keyPlaceholders = str_repeat('?,', count($keyValues) - 1) . '?';
        $allParams = array_merge($allParams, $keyValues);
        
        $sql = "UPDATE {$this->table} SET " . implode(', ', $cases) . 
               " WHERE {$this->keyColumn} IN ($keyPlaceholders)";
        
        return $this->db->execute($sql, $allParams);
    }
}

// 测试批量更新
$batchUpdater = new BatchUpdater($db, 'posts', 'id');

// 方法1:逐条更新
$updates = [
    1 => ['title' => 'PHP高级教程', 'status' => 'published'],
    2 => ['title' => 'MySQL优化指南', 'status' => 'published']
];

$affected = $batchUpdater->updateMultiple($updates);
echo "批量更新(逐条),影响行数: $affected\n";

// 方法2:CASE WHEN批量更新
$batchData = [
    ['id' => 3, 'title' => 'JavaScript高级编程', 'status' => 'published'],
    ['id' => 4, 'title' => 'CSS3动画设计', 'status' => 'published']
];

$affected = $batchUpdater->updateBatch($batchData, ['title', 'status']);
echo "批量更新(CASE WHEN),影响行数: $affected\n";

echo "\n--- 乐观锁更新 ---\n";

// 乐观锁更新示例
class OptimisticLockUpdater {
    private $db;
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function updateWithVersion($table, $id, $data, $currentVersion) {
        // 添加版本号到更新数据
        $data['version'] = $currentVersion + 1;
        $data['updated_at'] = date('Y-m-d H:i:s');
        
        $setParts = [];
        $params = [];
        
        foreach ($data as $column => $value) {
            $setParts[] = "$column = ?";
            $params[] = $value;
        }
        
        $params[] = $id;
        $params[] = $currentVersion;
        
        $sql = "UPDATE $table SET " . implode(', ', $setParts) . 
               " WHERE id = ? AND version = ?";
        
        $affected = $this->db->execute($sql, $params);
        
        if ($affected === 0) {
            throw new RuntimeException('记录已被其他用户修改,请刷新后重试');
        }
        
        return $affected;
    }
}

// 为用户表添加版本字段(仅演示,实际应用中应该在建表时添加)
try {
    $db->execute("ALTER TABLE users ADD COLUMN version INTEGER DEFAULT 1");
    echo "✓ 为用户表添加版本字段\n";
} catch (PDOException $e) {
    // 字段可能已存在
    echo "版本字段可能已存在\n";
}

$optimisticUpdater = new OptimisticLockUpdater($db);

// 模拟乐观锁更新
$user = $db->fetchOne("SELECT * FROM users WHERE username = ?", ['zhangsan']);
if ($user) {
    try {
        $affected = $optimisticUpdater->updateWithVersion(
            'users',
            $user['id'],
            ['email' => 'zhangsan_optimistic@example.com'],
            $user['version'] ?? 1
        );
        echo "✓ 乐观锁更新成功,影响行数: $affected\n";
    } catch (RuntimeException $e) {
        echo "✗ 乐观锁更新失败: " . $e->getMessage() . "\n";
    }
}
?>

10.2.4 删除操作(Delete)

<?php
// 删除操作
echo "=== 删除操作 ===\n";

echo "--- 基础删除 ---\n";

// 先插入一些测试数据
$testUsers = [
    ['testuser1', 'test1@example.com', password_hash('123456', PASSWORD_DEFAULT)],
    ['testuser2', 'test2@example.com', password_hash('123456', PASSWORD_DEFAULT)],
    ['testuser3', 'test3@example.com', password_hash('123456', PASSWORD_DEFAULT)]
];

foreach ($testUsers as $user) {
    $db->execute("INSERT INTO users (username, email, password) VALUES (?, ?, ?)", $user);
}
echo "✓ 插入测试用户数据\n";

// 单条删除
$deleteSQL = "DELETE FROM users WHERE username = ?";
$result = $db->execute($deleteSQL, ['testuser1']);
echo "删除用户testuser1,影响行数: $result\n";

// 条件删除
$deleteSQL = "DELETE FROM users WHERE username LIKE ?";
$result = $db->execute($deleteSQL, ['testuser%']);
echo "删除所有testuser开头的用户,影响行数: $result\n";

echo "\n--- 删除构建器 ---\n";

// 删除构建器类
class DeleteBuilder {
    private $db;
    private $table;
    private $where = [];
    private $params = [];
    
    public function __construct($db, $table) {
        $this->db = $db;
        $this->table = $table;
    }
    
    public function where($column, $operator, $value = null) {
        if ($value === null) {
            $value = $operator;
            $operator = '=';
        }
        
        $this->where[] = "$column $operator ?";
        $this->params[] = $value;
        return $this;
    }
    
    public function whereIn($column, $values) {
        $placeholders = str_repeat('?,', count($values) - 1) . '?';
        $this->where[] = "$column IN ($placeholders)";
        $this->params = array_merge($this->params, $values);
        return $this;
    }
    
    public function whereBetween($column, $min, $max) {
        $this->where[] = "$column BETWEEN ? AND ?";
        $this->params[] = $min;
        $this->params[] = $max;
        return $this;
    }
    
    public function toSQL() {
        $sql = 'DELETE FROM ' . $this->table;
        
        if (!empty($this->where)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->where);
        }
        
        return $sql;
    }
    
    public function execute() {
        return $this->db->execute($this->toSQL(), $this->params);
    }
}

// 插入更多测试数据
$morePosts = [
    [1, '待删除文章1', '内容1', 'draft'],
    [1, '待删除文章2', '内容2', 'draft'],
    [2, '待删除文章3', '内容3', 'draft']
];

foreach ($morePosts as $post) {
    $db->execute("INSERT INTO posts (user_id, title, content, status) VALUES (?, ?, ?, ?)", $post);
}

// 使用删除构建器
$deleteBuilder = new DeleteBuilder($db, 'posts');
$result = $deleteBuilder->where('status', 'draft')
                        ->where('user_id', 1)
                        ->execute();

echo "删除用户1的草稿文章,影响行数: $result\n";

echo "\n--- 软删除 ---\n";

// 软删除实现
class SoftDeleteManager {
    private $db;
    private $deletedAtColumn;
    
    public function __construct($db, $deletedAtColumn = 'deleted_at') {
        $this->db = $db;
        $this->deletedAtColumn = $deletedAtColumn;
    }
    
    public function softDelete($table, $id) {
        $sql = "UPDATE $table SET {$this->deletedAtColumn} = CURRENT_TIMESTAMP WHERE id = ? AND {$this->deletedAtColumn} IS NULL";
        return $this->db->execute($sql, [$id]);
    }
    
    public function restore($table, $id) {
        $sql = "UPDATE $table SET {$this->deletedAtColumn} = NULL WHERE id = ?";
        return $this->db->execute($sql, [$id]);
    }
    
    public function forceDelete($table, $id) {
        $sql = "DELETE FROM $table WHERE id = ?";
        return $this->db->execute($sql, [$id]);
    }
    
    public function findWithTrashed($table, $id) {
        $sql = "SELECT * FROM $table WHERE id = ?";
        return $this->db->fetchOne($sql, [$id]);
    }
    
    public function findOnlyTrashed($table, $id) {
        $sql = "SELECT * FROM $table WHERE id = ? AND {$this->deletedAtColumn} IS NOT NULL";
        return $this->db->fetchOne($sql, [$id]);
    }
    
    public function getAllActive($table) {
        $sql = "SELECT * FROM $table WHERE {$this->deletedAtColumn} IS NULL";
        return $this->db->fetchAll($sql);
    }
    
    public function getAllTrashed($table) {
        $sql = "SELECT * FROM $table WHERE {$this->deletedAtColumn} IS NOT NULL";
        return $this->db->fetchAll($sql);
    }
}

// 为表添加软删除字段
try {
    $db->execute("ALTER TABLE posts ADD COLUMN deleted_at DATETIME NULL");
    echo "✓ 为文章表添加软删除字段\n";
} catch (PDOException $e) {
    echo "软删除字段可能已存在\n";
}

$softDeleteManager = new SoftDeleteManager($db);

// 测试软删除
$posts = $db->fetchAll("SELECT id, title FROM posts WHERE deleted_at IS NULL LIMIT 2");
if (!empty($posts)) {
    $postId = $posts[0]['id'];
    $postTitle = $posts[0]['title'];
    
    // 软删除
    $result = $softDeleteManager->softDelete('posts', $postId);
    echo "软删除文章《{$postTitle}》,影响行数: $result\n";
    
    // 查看软删除的记录
    $trashedPost = $softDeleteManager->findOnlyTrashed('posts', $postId);
    if ($trashedPost) {
        echo "✓ 找到软删除的文章: {$trashedPost['title']}\n";
    }
    
    // 恢复软删除
    $result = $softDeleteManager->restore('posts', $postId);
    echo "恢复文章《{$postTitle}》,影响行数: $result\n";
}

echo "\n--- 级联删除 ---\n";

// 级联删除管理器
class CascadeDeleteManager {
    private $db;
    private $relationships = [];
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function addRelationship($parentTable, $childTable, $foreignKey) {
        if (!isset($this->relationships[$parentTable])) {
            $this->relationships[$parentTable] = [];
        }
        $this->relationships[$parentTable][] = [
            'table' => $childTable,
            'foreign_key' => $foreignKey
        ];
    }
    
    public function cascadeDelete($table, $id) {
        $this->db->beginTransaction();
        
        try {
            // 递归删除相关记录
            $this->deleteRecursive($table, $id);
            
            $this->db->commit();
            return true;
            
        } catch (Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }
    
    private function deleteRecursive($table, $id) {
        // 先删除子记录
        if (isset($this->relationships[$table])) {
            foreach ($this->relationships[$table] as $relationship) {
                $childTable = $relationship['table'];
                $foreignKey = $relationship['foreign_key'];
                
                // 查找所有子记录
                $children = $this->db->fetchAll(
                    "SELECT id FROM $childTable WHERE $foreignKey = ?",
                    [$id]
                );
                
                // 递归删除每个子记录
                foreach ($children as $child) {
                    $this->deleteRecursive($childTable, $child['id']);
                }
            }
        }
        
        // 最后删除当前记录
        $this->db->execute("DELETE FROM $table WHERE id = ?", [$id]);
    }
}

// 设置级联关系
$cascadeManager = new CascadeDeleteManager($db);
$cascadeManager->addRelationship('users', 'posts', 'user_id');

// 测试级联删除(注意:这会删除用户及其所有文章)
$testUser = $db->fetchOne("SELECT id, username FROM users WHERE username LIKE 'newuser%' LIMIT 1");
if ($testUser) {
    echo "准备级联删除用户: {$testUser['username']}\n";
    
    // 查看该用户的文章数
    $postCount = $db->fetchOne("SELECT COUNT(*) as count FROM posts WHERE user_id = ?", [$testUser['id']]);
    echo "该用户有 {$postCount['count']} 篇文章\n";
    
    try {
        $cascadeManager->cascadeDelete('users', $testUser['id']);
        echo "✓ 级联删除成功\n";
    } catch (Exception $e) {
        echo "✗ 级联删除失败: " . $e->getMessage() . "\n";
    }
}

echo "\n--- 批量删除 ---\n";

// 批量删除类
class BatchDeleter {
    private $db;
    private $batchSize;
    
    public function __construct($db, $batchSize = 1000) {
        $this->db = $db;
        $this->batchSize = $batchSize;
    }
    
    public function deleteByIds($table, $ids) {
        if (empty($ids)) {
            return 0;
        }
        
        $totalDeleted = 0;
        $batches = array_chunk($ids, $this->batchSize);
        
        foreach ($batches as $batch) {
            $placeholders = str_repeat('?,', count($batch) - 1) . '?';
            $sql = "DELETE FROM $table WHERE id IN ($placeholders)";
            $deleted = $this->db->execute($sql, $batch);
            $totalDeleted += $deleted;
        }
        
        return $totalDeleted;
    }
    
    public function deleteByCondition($table, $condition, $params = []) {
        // 分批删除以避免锁定太多行
        $totalDeleted = 0;
        
        do {
            $sql = "DELETE FROM $table WHERE $condition LIMIT {$this->batchSize}";
            $deleted = $this->db->execute($sql, $params);
            $totalDeleted += $deleted;
        } while ($deleted > 0);
        
        return $totalDeleted;
    }
}

$batchDeleter = new BatchDeleter($db, 100);

// 创建一些测试数据
for ($i = 1; $i <= 5; $i++) {
    $db->execute(
        "INSERT INTO posts (user_id, title, content, status) VALUES (?, ?, ?, ?)",
        [1, "批量删除测试文章$i", "内容$i", 'draft']
    );
}

// 获取要删除的ID
$posts = $db->fetchAll("SELECT id FROM posts WHERE title LIKE '批量删除测试文章%'");
$ids = array_column($posts, 'id');

if (!empty($ids)) {
    $deleted = $batchDeleter->deleteByIds('posts', $ids);
    echo "批量删除测试文章,删除数量: $deleted\n";
}

// 按条件批量删除
$deleted = $batchDeleter->deleteByCondition('posts', 'status = ?', ['draft']);
echo "按条件批量删除草稿文章,删除数量: $deleted\n";
?>