必须使用abstract修饰dalexception基类,以防止业务层直接实例化而破坏异常分类语义;其下分connectionfailedexception、queryfailedexception、recordnotfoundexception三个子类,分别对应连接失败、查询执行失败和记录不存在,并通过pdo错误码精准识别,避免字符串匹配。

当PHP数据访问层(DAL)中发生数据库连接失败、SQL语法错误或记录不存在等异常时,需要统一捕获并转换为语义明确、可被业务层精准识别的自定义异常,避免原始PDOException暴露敏感信息或导致调用方无法区分错误类型。
定义基础异常类与分层异常体系
创建一个抽象基类 DalException,所有DAL相关异常都继承它,确保类型可识别且不与框架或PHP原生异常混杂。
在项目 src/Exception/ 目录下新建 DalException.php 文件,写入:
abstract class DalException extends \Exception {}
接着定义三个具体子类:ConnectionFailedException(连接失败)、QueryFailedException(查询执行失败)、RecordNotFoundException(查无此记录)。每个类只继承 DalException,不重写构造函数——让父类统一处理消息、代码和上下文。
【必须使用 abstract 修饰基类】 否则业务层可能直接 new 出基类实例,破坏异常分类语义。
封装PDO操作并触发对应异常
在DAL核心类(如 DalConnection)中,所有PDO操作都需包裹 try-catch,并按错误特征抛出对应子类异常。
方法一:连接初始化时捕获 PDO 构造异常
在 __construct() 中尝试 new PDO,捕获 PDOException 后判断 $e->getCode() 是否为 '08001'(连接拒绝)或 'HY000'(通用连接错误),是则抛出 ConnectionFailedException;其他连接级错误也归为此类。
方法二:执行查询时区分错误类型
调用 $pdo->prepare() 或 $pdo->query() 后,检查 $pdo->errorCode():若为 '42000'(语法错误)或 '42S02'(表不存在),抛 QueryFailedException;若执行成功但 fetch() 返回 false 且明确知道应有单条记录(如 findById),则抛 RecordNotFoundException。
注意:不要依赖 getMessage() 字符串匹配来判断错误类型——MySQL 和 PostgreSQL 的错误提示格式不同,极易误判。
统一异常转换中间件(可选但推荐)
若项目已接入PSR-15中间件机制,可在请求生命周期早期注册一个DAL异常处理器。
第一步:编写 DalExceptionHandler 类,实现 MiddlewareInterface。
第二步:在 process() 方法中,调用 $handler->handle($request) 后捕获异常。
第三步:对捕获到的 DalException 子类,根据类型设置不同HTTP状态码:ConnectionFailedException → 503,QueryFailedException → 400,RecordNotFoundException → 404。
第四步:将原始异常信息剥离,仅保留 getMessage() 和自定义错误码(如 ERR_DAL_CONN_FAIL),再封装成JSON响应返回。
这一步不修改异常本身,只改变对外输出形态——内部日志仍记录完整堆栈,前端只看到脱敏后的结构化错误。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











