TP8异常处理必须通过app/provider.php注册实现ExceptionHandlerInterface的类,禁用config/app.php的exception_handle配置;render()需显式注入Request并返回Response,report()必须调用parent::report()以保障日志链路完整。

ThinkPHP 8.0 的异常处理不再走 config/app.php 的 exception_handle 配置,也不再依赖 think\exception\Handle 构造函数传参——所有旧版写法都会静默失效或直接报错。
TP8 异常处理器必须用服务提供者注册
框架启动时只从 app/provider.php 读取异常处理器绑定,其他地方的配置(如 config/exception.php 或 app.php 中的 exception_handle)完全被忽略。如果你沿用 TP6 写法,在 config/app.php 里配了 exception_handle,应用根本不会加载你的类,更不会报错提示,而是退回到框架默认行为。
正确做法是:
- 确保
app/exception/Handler.php类实现think\contract\ExceptionHandlerInterface - 在
app/provider.php中添加:return [ 'think\exception\Handle' => app\exception\Handler::class, ]; - 类中
render()方法签名必须为public function render(Request $request, Throwable $e): Response,Request不可省略或用app('request')替代
report() 不调用 parent::report() 就等于没记日志
很多开发者重写 report() 后只做自己的逻辑(比如发钉钉、写自定义文件),却忘了调用 parent::report($exception)。结果是:Sentry、Log channel、框架内置错误统计全部失效,$ignoreReport 也失去作用。
立即学习“PHP免费学习笔记(深入)”;
常见错误现象:
- 数据库连接失败(
PDOException)不进日志,但页面显示“500 Internal Server Error” - 自定义业务异常进了日志,但
ValidateException却没记录——因为后者在$ignoreReport里,而你没走父类判断逻辑 - 日志里只有空行或时间戳,没有堆栈、文件、上下文
安全做法是:先判断是否该忽略,再决定是否调用父类;不要覆盖,而是增强:
public function report(Throwable $exception): void
{
if ($this->isIgnoreReport($exception)) {
return;
}
// 自定义上报
if ($exception instanceof BusinessException) {
\think\facade\Log::error('BUSINESS_ERROR: ' . $exception->getMessage(), [
'trace' => $exception->getTraceAsString(),
]);
}
// 必须保留这句,否则框架日志链路中断
parent::report($exception);
}
render() 返回前别 echo/die/exit,也别漏判请求类型
render() 是响应生成环节,返回值必须是 Response 实例。任何直接输出(echo)、终止(die、exit)或返回字符串/数组的行为,都会导致 Swoole/Worker 模式下进程崩溃,或 FPM 下出现 “headers already sent” 错误。
另一个高频坑是:没区分 API 和 Web 请求,把 JSON 接口也渲染成 HTML 模板。TP8 默认会根据 Accept 头自动切换格式,但一旦你在 render() 里硬编码 view() 或 json(),就绕过了这个逻辑。
建议检查点:
- 用
$request->isAjax()或$request->header('accept')显式判断请求类型 - 对
HttpException子类(如RouteNotFoundException)优先走parent::render(),避免重复实现 404/500 页面 - 非 HTTP 异常(如
ValidateException、BusinessException)应统一返回 JSON,状态码设为 422 或 400
调试模式开启后,.env 里 APP_DEBUG=true 不代表万事大吉
TP8 的调试模式不仅控制异常页面展示,还影响 Trace、SQL 日志、变量 dump 等行为。但很多人忽略了两点:
-
APP_DEBUG=true仅在.env生效,如果部署时用了php think run -v或容器环境变量覆盖,.env会被忽略 - 即使开启调试,若
exception_tmpl配置指向一个不存在的路径(比如拼错为think_excepion.tpl),框架会静默回退到空白白屏,而不是报错提示
验证方式很简单:在控制器里手动抛出异常 throw new \Exception('test');,看是否显示带堆栈的蓝色异常页。如果不显示,优先检查 APP_DEBUG 是否真生效、exception_tmpl 路径是否存在、以及 app/exception/Handler.php 是否被正确加载(可用 var_dump(class_exists('app\exception\Handler')) 快速验证)。



















