pdo是数据库访问抽象层,提供统一接口操作多种数据库;相比mysql_*(已废弃)和mysqli,它更安全(支持预处理防注入)、可移植性强、面向对象且支持命名参数。

PDO 是什么,为什么用它而不是 mysql_* 或 mysqli?
PDO(PHP Data Objects)是一套数据库访问抽象层,不是数据库驱动本身,而是提供统一接口操作不同数据库(MySQL、PostgreSQL、SQLite、Oracle 等)。它不处理 SQL 解析或数据库特有功能,只负责执行语句、绑定参数、获取结果。
相比已废弃的 mysql_* 函数(PHP 7.0 移除),PDO 更安全、更灵活;相比原生 mysqli,PDO 的优势在于:
- 支持预处理语句(防止 SQL 注入的核心机制)
- 数据库可移植性强(换库只需改 DSN,逻辑代码基本不动)
- 面向对象设计,API 一致,异常模式可选
- 支持命名参数(:name)、问号占位符(?),写法更清晰
如何正确创建 PDO 实例并处理连接错误?
关键不是简单 new PDO(),而是要显式配置选项并捕获异常:
- 设置
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,让错误抛出异常而非静默失败或返回 false - 禁用模拟预处理(
PDO::ATTR_EMULATE_PREPARES => false),确保预处理真正在数据库端执行,避免绕过类型检查 - 设置默认获取模式(如
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC),省去每次 fetch 时指定 - 连接失败时 try/catch 捕获
PDOException,不要依赖@抑制符
示例:
phptry {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
error_log('DB connection failed: ' . $e->getMessage());
die('Service unavailable');
}
?>
预处理与参数绑定:防注入的关键实操要点
预处理 ≠ 写了 prepare 就安全。真正起作用的是“参数绑定”环节:
立即学习“PHP免费学习笔记(深入)”;
- 永远用
bindValue()或bindParam()传参,不要拼接字符串 -
bindValue()传值(适合大多数场景);bindParam()传变量引用(适合循环复用同一语句,如批量插入) - 类型明确时指定数据类型(如
PDO::PARAM_INT),尤其对数字字段,避免字符串隐式转换引发意外 - 命名参数和问号参数不能混用;命名参数可乱序,问号参数必须严格按顺序
反例(危险):
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = " . $_GET['id']); // ❌ 拼接=SQL注入
正例:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->bindValue(':id', $_GET['id'], PDO::PARAM_INT); // ✅ 安全且类型明确
事务控制与错误回滚必须成对出现
PDO 默认自动提交(autocommit),单条语句成功即生效。涉及多步操作(如转账、订单+库存)必须手动管理事务:
- 调用
$pdo->beginTransaction()开启事务 - 所有操作完成后,显式调用
$pdo->commit() - 任意一步失败(抛异常或判断条件),立即
$pdo->rollback() - 事务中若发生未捕获异常,PDO 不会自动回滚,必须在 catch 块里写 rollback
常见疏漏:只写了 beginTransaction 和 commit,没写 rollback 分支,导致部分写入残留。
常见陷阱与性能提醒
面试常被追问细节,这些点容易答不全:
-
字符集必须在 DSN 中声明(如
charset=utf8mb4),仅在 SQL 里执行SET NAMES不可靠,PDO 可能忽略 - fetchAll() 一次性取全部结果,大数据量易内存溢出;应优先用 foreach 遍历 statement 或 fetch() 逐行处理
-
PDOStatement 对象不可重用:执行完后需重新 prepare,或复用前 clear 旧绑定(
$stmt->clear()) - 长连接不等于连接池:PHP-FPM 下每个 worker 进程维护独立连接,PDO 本身无连接池,高并发需靠 MySQL 连接数配置和应用层连接复用策略











