PHP PDO 是最安全灵活的数据库操作方式,支持统一接口、预处理语句、事务控制和异常处理;通过连接配置、增删改查、事务管理及健壮性优化,可全面保障数据安全与一致性。

PHP PDO 是操作数据库最安全、最灵活的方式之一,它统一了不同数据库的访问接口,支持预处理语句、事务控制和多种错误处理模式。下面通过一个完整的用户管理实战案例,带你从连接、查询、增删改到异常处理,一步到位掌握 PDO 核心用法。
一、PDO 数据库连接与基础配置
连接 MySQL(或其他数据库)前,需确保已启用 pdo_mysql 扩展。连接时推荐使用 DSN 字符串 + 选项数组,禁用模拟预处理、设置错误模式为异常,便于后续统一捕获错误:
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, 'username', 'password', $options);
} catch (PDOException $e) {
die("连接失败:" . $e->getMessage());
}- charset=utf8mb4 支持 emoji 和完整 Unicode;
- PDO::ATTR_EMULATE_PREPARES => false 强制使用真实预处理,防止 SQL 注入绕过;
- FETCH_ASSOC 让 fetch() 默认返回关联数组,更符合 PHP 开发习惯。
二、安全执行查询:预处理 + 绑定参数
所有带用户输入的操作都必须用预处理语句。例如根据用户名查用户信息:
$username = $_GET['user'] ?? '';
$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
<p>if ($user) {
echo "欢迎," . htmlspecialchars($user['username']);
} else {
echo "用户不存在";
}</p>- 问号占位符(?)或命名参数(:name)均可,后者更适合复杂 SQL;
- execute() 传入数组,PDO 自动转义并绑定,无需手动过滤;
- 即使 $username 是
'admin' OR 1=1,也不会导致注入——这是预处理的本质保障。
三、插入、更新与删除:一行代码防错
插入新用户示例(含密码哈希):
立即学习“PHP免费学习笔记(深入)”;
$stmt = $pdo->prepare(
"INSERT INTO users (username, email, password_hash, created_at)
VALUES (?, ?, ?, NOW())"
);
$passwordHash = password_hash($_POST['password'], PASSWORD_ARGON2ID);
$stmt->execute([
$_POST['username'],
$_POST['email'],
$passwordHash
]);
$userId = $pdo->lastInsertId(); // 获取自增 ID- UPDATE 和 DELETE 同样用 prepare + execute,WHERE 条件也必须参数化;
- 避免拼接 SQL 字符串,哪怕只是拼接字段名——如需动态字段,应白名单校验后拼接;
- 执行成功后可用 rowCount() 判断影响行数,比如 UPDATE 返回 0 表示无匹配记录。
四、事务处理:保证数据一致性
转账操作需原子性:扣款 + 入账,任一失败则全部回滚:
try {
$pdo->beginTransaction();
<pre class="brush:php;toolbar:false;">$pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE user_id = ?")->execute([100, 1]);
$pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE user_id = ?")->execute([100, 2]);
$pdo->commit();
echo "转账成功";} catch (Exception $e) { $pdo->rollback(); echo "转账失败:" . $e->getMessage(); }
- 事务中所有语句共享同一连接上下文,不能跨多个 $pdo 实例;
- 一旦调用 beginTransaction(),后续操作直到 commit() 或 rollback() 才真正生效;
- 建议在 try/catch 中包裹整个事务逻辑,异常时自动回滚。
五、常见问题与健壮性增强
实际项目中还需注意:
- 连接复用:将 $pdo 封装为单例或依赖注入容器管理,避免重复连接;
-
长连接超时:MySQL 默认 wait_timeout=28800 秒,可在 DSN 加
&mysql.connect_timeout=5控制; -
调试技巧:临时开启
PDO::ATTR_EMULATE_PREPARES => true并用debugDumpParams()查看绑定值; -
多库支持:只需更换 DSN(如
pgsql:host=...或sqlite:/path/db.sqlite),其余代码几乎不用改。











