You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yii2中如何自定义严重异常的处理逻辑?

Yii2中如何自定义严重异常的处理逻辑?

我完全理解你的困惑——Yii2自带的错误/异常视图区分确实容易让人混淆,而且默认配置下,一些严重的PHP错误(比如未定义属性、对象转数组这类)会绕过你自定义的site/error动作,直接用系统的exception.php显示,这确实很头疼。

为什么会出现这个情况?

核心原因有两个:

  1. Yii2的errorHandler组件默认在YII_DEBUG=true时,会优先显示带调用栈的exception.php视图,不管你是否设置了errorAction;
  2. 像Undefined property这类错误属于PHP原生的Error(而非Exception),在PHP7+里它们实现了Throwable接口,但你的原代码只处理了特定的Exception子类,没有覆盖所有Throwable类型。

解决步骤:完全接管所有错误/异常处理

下面是一套完整的配置和代码改造方案,能让所有错误(包括严重的PHP原生错误)都走你自定义的处理逻辑:

1. 调整errorHandler组件配置

在你的应用配置文件(比如web.php)里,修改errorHandler的配置,强制关闭系统自带的异常显示,并确保所有错误都指向你的自定义动作:

return [
    'components' => [
        'errorHandler' => [
            // 指向你的自定义错误处理动作
            'errorAction' => 'site/error',
            // 强制关闭系统自带的异常视图(不管DEBUG模式)
            'displayException' => false,
            // 捕获所有级别错误
            'errorLevel' => E_ALL,
            // 如果需要在DEBUG模式下也隐藏调用栈(可选)
            'showExceptionDetails' => false,
        ],
    ],
];

2. 完善SiteController的actionError方法

修改你的actionError,覆盖所有Throwable类型(包含Exception和PHP原生Error),同时完善日志记录和统一响应格式:

public function actionError()
{
    $handler = Yii::$app->errorHandler;
    $exception = $handler->exception;

    // 用Yii日志组件记录详细错误信息(比error_log更灵活,可配置日志目标)
    Yii::error([
        'error_type' => get_class($exception),
        'message' => $exception->getMessage(),
        'status_code' => $exception instanceof \yii\web\HttpException ? $exception->statusCode : 500,
        'file' => $exception->getFile(),
        'line' => $exception->getLine(),
        'trace' => $exception->getTraceAsString(),
    ], 'custom_error_handler');

    // 设置响应状态码
    Yii::$app->response->statusCode = $exception instanceof \yii\web\HttpException 
        ? $exception->statusCode 
        : 500;

    // 准备统一的JSON响应数据
    $data = new \stdClass();
    $data->error = true;

    // 根据环境返回不同的错误信息(开发环境返回详细信息,生产环境返回通用提示)
    if (YII_DEBUG) {
        $data->message = $exception->getMessage();
        $data->details = [
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString(),
        ];
    } else {
        // 针对不同错误类型返回更友好的生产环境提示
        if ($exception instanceof \yii\web\NotFoundHttpException) {
            $data->message = '请求的资源不存在';
        } elseif ($exception instanceof \yii\web\UnauthorizedHttpException) {
            $data->message = '未授权访问';
        } else {
            $data->message = '服务器内部错误';
        }
    }

    // 强制设置响应格式为JSON,适配前端/移动端
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return $data;
}

3. 额外注意:处理PHP致命错误

极少数情况下,PHP的致命错误(比如内存耗尽)可能无法被Yii的errorHandler捕获,这时可以在入口文件(web/index.php)的最顶部添加全局错误捕获:

// 在Yii初始化之前注册全局Throwable处理器
set_exception_handler(function (\Throwable $exception) {
    // 记录日志
    error_log('Fatal error: ' . $exception->getMessage() . ' in ' . $exception->getFile() . ':' . $exception->getLine());
    // 返回JSON响应
    http_response_code(500);
    echo json_encode([
        'error' => true,
        'message' => YII_DEBUG ? $exception->getMessage() : '服务器内部错误'
    ]);
    exit;
});

// 然后继续原来的Yii初始化代码
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
$config = require __DIR__ . '/../config/web.php';
(new yii\web\Application($config))->run();

这样改造后,所有错误都会走你的自定义逻辑:

  • 不管是HTTP异常(401、404)还是PHP原生错误(未定义属性、对象转数组),都会被捕获;
  • 错误信息会被详细记录,方便排查;
  • 前端/移动端会收到统一格式的JSON响应,便于处理;
  • 开发环境和生产环境的错误展示逻辑分离,兼顾调试和安全。

备注:内容来源于stack exchange,提问作者TomR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 08:59:36