
本文详解如何通过 pdo 的 try-catch 捕获 mysql 唯一键(如手机号)重复插入异常,并正确区分“已存在”与“新增成功”两种状态,避免因逻辑误判导致成功提示不显示的问题。
在使用 PDO 向 MySQL 插入用户订阅数据(如姓名、手机号、留言)时,为防止重复注册,常对 telephone 字段设置 UNIQUE KEY 约束。但直接抛出原始 PDOException(如 SQLSTATE[23000]: Integrity constraint violation: 1062)极不友好,需个性化提示。常见误区是将“插入成功”的反馈写在 catch 的 else 分支中——这是错误的逻辑结构:catch 仅在异常发生时执行,而插入成功根本不会进入 catch,因此 else 永远不会被触发。
✅ 正确做法是:用标志位(flag)显式标记执行路径,确保无论是否抛出异常,都能统一处理响应:
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$phone = $_POST['telephone'] ?? '';
$name = $_POST['name'] ?? '';
$comment = $_POST['comment'] ?? '';
$exist = false; // 初始化标志:false 表示尚未存在,true 表示因唯一键冲突被拒绝
try {
$stmt = $pdo->prepare("INSERT INTO subscribers (name, telephone, comment) VALUES (?, ?, ?)");
$stmt->execute([$name, $phone, $comment]);
} catch (PDOException $e) {
error_log("DB Error: " . $e->getMessage());
// 仅当是唯一键冲突(SQLSTATE 23000)时才视为“已存在”
if ($e->getCode() === '23000' && strpos($e->getMessage(), 'telephone') !== false) {
$exist = true;
} else {
// 其他数据库异常(如连接失败、字段超长等),应记录并提示系统错误
echo '系统繁忙,请稍后重试。';
exit;
}
}
// 统一在 try-catch 外判断结果,逻辑清晰且覆盖所有分支
if ($exist) {
echo '该手机号已被注册,请勿重复提交。';
} else {
echo '感谢订阅!您已成功加入数据库。';
}
?>? 关键要点说明:
- PDO::ERRMODE_EXCEPTION 必须启用,否则 execute() 不会抛出异常,catch 将失效;
- strpos($e->getMessage(), 'telephone') 可增强健壮性,避免其他 23000 错误(如主键冲突)被误判;
- 成功提示绝不可放在 catch 内部,必须置于 try-catch 结构之外,作为最终状态输出;
- 生产环境建议使用更精细的错误分类(如 HTTP 状态码、前端 Toast 提示),而非直接 echo HTML 片段;
- 安全提醒:务必对 $_POST 数据进行过滤与验证(如 filter_var($phone, FILTER_SANITIZE_NUMBER_INT)),防止 SQL 注入或 XSS。
通过此方案,你既能精准拦截唯一键冲突,又能确保用户操作后获得明确、无歧义的反馈——这才是专业表单体验的核心所在。










