php8.0中自定义异常类必须继承exception或其子类,否则throw后无法被catch捕获;构造函数参数类型严格,$code必须为int,$previous后置且可为?throwable;应使用catch(throwable $e)兜底但需谨慎,避免静默吞错。

PHP8.0中自定义异常类必须继承 Exception 或其子类
PHP8.0 严格区分错误(Error)和异常(Exception),直接 throw 一个普通类或数组会报 Fatal error: Uncaught TypeError。自定义异常类不是“随便写个类就行”,它必须是 Exception 的合法子类,否则 catch 永远捕获不到。
常见错误现象:写了 class MyException { },然后 throw new MyException(),结果脚本直接终止,没进 catch 块。
- 正确写法:用
class MyException extends Exception {} - PHP8.0 开始,
Exception构造函数参数类型更严格,第 2 个参数($code)必须是int,传字符串会触发TypeError - 如果需要处理 PHP 内部错误(如
TypeError、ValueError),不能靠继承Exception,得用set_error_handler()+set_exception_handler()组合,或直接catch对应的Error类型
如何让自定义异常在 try/catch 中被精准捕获
关键不是“能不能 catch”,而是“catch 块是否匹配抛出的异常类型”。PHP8.0 支持多类型捕获,但顺序和继承关系必须对得上。
使用场景:你想区分数据库异常、验证异常、第三方 API 异常,各自走不同处理逻辑。
- 按继承层级组织:比如
ValidationException extends AppException,DatabaseException extends AppException,然后catch (AppException $e)可捕获所有子类 - 避免写
catch (Exception $e)在前,再写catch (MyException $e)在后——后者永远执行不到 - PHP8.0 允许联合类型捕获:
catch (ValidationException | DatabaseException $e),但注意这不会捕获它们的父类,也不含Error - 别忘了
finally不影响捕获逻辑,但它会在catch执行完后无条件运行
PHP8.0新增的 Throwable 接口与捕获范围的关系
Throwable 是 PHP7 引入、PHP8.0 完全落地的顶层接口,Exception 和所有 Error(如 TypeError、ParseError)都实现它。这意味着你不能再只依赖 Exception 来兜底所有“可抛出的东西”。
性能 / 兼容性影响:用 catch (Throwable $e) 能捕获一切,但代价是模糊了错误性质——你无法默认假设它是业务异常,也可能是致命语法错误。
- 线上环境不建议全局
catch (Throwable $e)后静默吞掉,至少要记录$e::class和$e->getTraceAsString() - 如果你的自定义异常类想同时兼容
Error场景,不能继承Error(它是 final 类),只能靠外层统一拦截 + 包装成你的异常类再重抛 -
set_exception_handler()回调接收的参数类型是Throwable,不是Exception,PHP8.0 下漏写类型声明会导致类型错误
实际例子:一个带上下文的 API 异常类
下面这个 ApiException 类在 PHP8.0 下能正常工作,包含必要字段、类型安全构造、且可被明确捕获:
class ApiException extends Exception
{
public function __construct(
string $message = '',
int $code = 400,
?Throwable $previous = null,
public array $context = []
) {
parent::__construct($message, $code, $previous);
}
}
使用时:
try {
throw new ApiException('User not found', 404, null, ['user_id' => 123]);
} catch (ApiException $e) {
http_response_code($e->getCode());
echo json_encode([
'error' => $e->getMessage(),
'context' => $e->context,
]);
}
容易踩的坑:PHP8.0 禁止在构造函数参数中给 $code 设默认值为 null(因为类型声明是 int),也不允许把 $context 放在 $previous 前面(违反参数顺序约束)。
真正麻烦的不是写这个类,而是后续所有调用方是否一致用 catch (ApiException $e),以及日志系统是否提取了 $e->context。这些细节一旦漏掉,自定义就只剩个空壳。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











