
本文详解如何通过 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。
通过此方案,你既能精准拦截唯一键冲突,又能确保用户操作后获得明确、无歧义的反馈——这才是专业表单体验的核心所在。