在 Yii2.0.55 中实现全局异常优雅降级,需禁用 YII_DEBUG、自定义 GracefulErrorHandler 覆盖 handleFatalError、统一通过 site/error 分发错误视图,并禁用输出缓冲与提前输出。

要在 Yii2.0.55 中实现全局异常的优雅降级,必须绕过默认调试页、屏蔽敏感堆栈、按异常类型分发响应,并确保生产环境不暴露任何内部信息。这要求精确控制 errorHandler 组件行为、严格校验 YII_DEBUG 状态、并主动拦截致命错误与 HTTP 异常分支。
确认基础配置与环境开关
打开 config/web.php,检查 components 下的 errorHandler 配置是否完整且仅在此处声明:
【'errorHandler' => ['errorAction' => 'site/error', 'class' => 'yii\web\ErrorHandler']】
确保 YII_DEBUG 在生产环境为 false —— 若为 true,无论其他配置如何,Yii 都会强制显示调试堆栈页,优雅降级完全失效。
验证当前环境:在入口文件 web/index.php 顶部添加临时判断(部署前必须删除):
if (YII_DEBUG) { die('DEBUG MODE ON — aborting graceful fallback'); }
定义 site/error 动作并分离视图逻辑
在 @app/controllers/SiteController.php 中,确保存在 actionError 方法:
public function actionError()
{
$exception = Yii::$app->errorHandler->exception;
if ($exception instanceof \yii\web\NotFoundHttpException) {
return $this->render('error-404', ['exception' => $exception]);
} elseif ($exception instanceof \yii\db\Exception) {
return $this->render('error-503', ['exception' => $exception]);
} else {
return $this->render('error-500', ['exception' => $exception]);
}
}
注意:不要依赖 Yii::$app->response->getStatusCode() 判断状态码,该值在错误流程中已被重写多次,不可靠。
在 @app/views/site/ 下创建三个对应视图:
error-404.php(纯静态 HTML,无 PHP 输出)、
error-503.php(含“服务暂时不可用”提示)、
error-500.php(通用错误页,禁止输出 $exception->getTraceAsString())。
接管致命错误与解析错误
Yii 默认的 renderException() 不处理 ParseError、Fatal Error,它们走 handleFatalError() 流程。必须自定义 errorHandler 类来覆盖该方法:
新建 @app/components/GracefulErrorHandler.php:
namespace app\components;
use yii\web\ErrorHandler as BaseErrorHandler;
class GracefulErrorHandler extends BaseErrorHandler
{
public function handleFatalError($error)
{
$this->logFatalError($error);
Yii::$app->response->statusCode = 500;
return $this->render('error-500');
}
protected function logFatalError($error)
{
Yii::error("FATAL: {$error['message']} in {$error['file']}:{$error['line']}", 'fatal');
}
}
然后在 config/web.php 的 components 中替换类名:
'errorHandler' => [
'class' => 'app\components\GracefulErrorHandler',
'errorAction' => 'site/error'
],
禁用输出缓冲干扰与提前响应终止
第一步:检查是否启用了 ob_start() 或第三方调试工具条(如 Yii Debug Toolbar),它们会在错误发生前写入响应头,导致后续 render() 输出被吞掉。
第二步:在 GracefulErrorHandler 的构造函数中强制设置 discardExistingOutput = true:
public function __construct($config = [])
{
$config['discardExistingOutput'] = true;
parent::__construct($config);
}
第三步:确保所有控制器动作、组件初始化、或 beforeAction 中没有 echo/print_r/var_dump/exit —— 这些操作一旦执行,就无法再调用 render() 返回 HTML。
最后一步:在 web.php 的 response 组件中关闭非必要格式支持,防止 JSON/XML 响应干扰 HTML 错误页渲染:
'response' => [
'formatters' => [
'html' => ['class' => 'yii\web\HtmlResponseFormatter'],
],
],


















