必须先设置pdo::attr_errmode为pdo::errmode_exception,否则try-catch捕不到pdoexception;默认php 8.0前为silent模式,仅设错误码不抛异常;php 8.0+虽默认exception,但旧配置或显式覆盖仍会导致失效。

必须先设置 PDO::ATTR_ERRMODE 为 PDO::ERRMODE_EXCEPTION,否则 try-catch 捕不到数据库异常——这是绝大多数人踩坑的第一步。
为什么 catch 不到 PDOException?
默认情况下(PHP 8.0 之前),PDO 使用 PDO::ERRMODE_SILENT,错误只设内部错误码,不抛异常;即使 PHP 8.0+ 默认改成了 EXCEPTION 模式,很多项目仍显式覆盖或沿用旧配置。没设这个属性,prepare()、execute() 失败只会返回 false,catch (PDOException $e) 完全无效。
- 检查连接后是否立刻调用了
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) - 确保该调用在任何 SQL 执行之前,且未被后续代码覆盖
- 如果用的是封装类(如 Laravel 的 DB 或自定义 PDO 封装),确认其构造逻辑里已设置该属性
如何区分唯一约束冲突和其他数据库错误?
插入重复数据触发的异常,其 $e->getCode() 是 "23000"(SQLSTATE),而 MySQL 具体错误码可能是 1062(Duplicate entry)。不能只靠 getMessage() 字符串匹配,因为语言/环境可能变化。
- 在
catch (PDOException $e)中,用$e->getCode() === '23000'判断是否为完整性约束类错误 - 进一步用
$e->errorInfo[1]获取 MySQL 原生错误码(如1062),用于精准分支处理 - 例如邮箱重复注册时,可返回用户提示“该邮箱已被注册”,而不是泛泛的“保存失败”
- 注意:不同数据库驱动的 SQLSTATE 映射不同,MySQL 的
1062对应23000,PostgreSQL 的重复键是23505
finally 里要不要 close 连接?
不需要手动 $pdo = null 或 $pdo->close()。PDO 连接是资源句柄,PHP 在脚本结束或对象销毁时自动释放;finally 里强行置空反而可能干扰连接池或长连接复用逻辑。
-
finally应只做真正“必须执行”的清理:比如关闭fopen()文件句柄、curl_close()、临时文件unlink() - 如果用了事务,
finally里不要无条件rollback()——得先判断是否已commit,否则可能回滚掉已提交的成功操作 - 更安全的做法是:在
try开头beginTransaction(),成功则commit(),异常则rollback(),不用依赖finally
最易被忽略的一点:全局未捕获的 PDOException 会触发 Fatal error 并暴露堆栈——哪怕你写了大量 try-catch,漏掉一处(比如构造函数里 new PDO 时没包),生产环境就可能泄露数据库地址和表结构。务必配一个 set_exception_handler() 做兜底,且 php.ini 中关掉 display_errors。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











