
本文介绍如何在 php pdo 中优雅捕获 mysql 唯一键(如手机号)重复插入导致的 `1062` 错误,并精准显示用户友好的提示信息,避免原始异常堆栈暴露给前端,同时确保成功插入后也能正确反馈。
在使用 PDO 向 MySQL 插入用户注册数据(如姓名、手机号、留言)时,为防止重复提交,常对 telephone 字段设置 UNIQUE KEY 约束。但当插入已存在的手机号时,PDO 默认抛出 PDOException,其原始错误信息(如 SQLSTATE[23000]: Integrity constraint violation: 1062...)不仅对用户不友好,还可能泄露数据库结构细节。
你尝试用 try/catch 捕获异常并区分逻辑分支是正确的方向,但原代码中将「插入成功」的提示放在 catch 的 else 分支存在根本性逻辑错误:只有发生异常时才会进入 catch 块;插入成功时程序正常执行 try 块末尾,直接跳过整个 catch,因此 else 永远不会执行。
✅ 正确做法是:将成功提示移出 catch,作为 try 执行完成后的默认路径。推荐使用标志位(flag)清晰分离两种状态:
prepare("INSERT INTO subscribers (name, telephone, comment) VALUES (?, ?, ?)");
$stmt->execute([$name, $telephone, $comment]);
$success = true; // 插入成功,标记为 true
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
if ($e->getCode() === '23000' && strpos($e->getMessage(), 'Duplicate entry') !== false) {
echo '该手机号已被注册,请勿重复提交。';
} else {
// 其他数据库错误(如连接失败、字段超长等)
echo '系统繁忙,请稍后重试。';
}
}
// ✅ 此处判断是否成功执行(未抛异常),而非在 catch 中 else
if ($success) {
echo '感谢订阅!信息已成功保存。';
}
?>? 关键注意事项:
- 不要依赖 $e->getCode() 单独判断唯一键冲突:MySQL 的 23000 是通用完整性错误码,可能涵盖外键、非空约束等。建议结合 getMessage() 中的关键字(如 'Duplicate entry')双重校验,提升健壮性;
- 始终记录原始异常(如 error_log()):便于后续排查非预期错误;
- 避免在生产环境直接输出调试信息或堆栈:所有面向用户的提示应统一、简洁、无技术术语;
- 考虑事务与并发场景:高并发下仍可能出现“检查-插入”竞态,唯一索引约束 + 原子性 INSERT 是最可靠的防重手段,无需额外 SELECT 验证。
通过上述结构,你既能拦截并美化唯一键冲突提示,又能确保每次成功插入后稳定输出确认信息,实现专业、可靠且用户体验良好的表单反馈机制。










