ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Yii 2 错误处理完全指南:ErrorHandler 组件、异常页面定制与多格式错误响应

Yii 2 错误处理完全指南:ErrorHandler 组件、异常页面定制与多格式错误响应 Yii 2 错误处理完全指南ErrorHandler 组件、异常页面定制与多格式错误响应【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2本篇技术指南以 Yii 2 内置的yii\web\ErrorHandler错误处理器为核心系统讲解它在 Web 应用请求生命周期参见 runtime-overview.md中如何接管 PHP 错误与异常从“非致命错误转异常”的底层机制、YII_DEBUG与YII_ENABLE_ERROR_HANDLER常量的行为差异到使用errorAction自定义错误页面、以及按response组件格式输出 HTML / JSON / RAW 错误响应的完整方案。读完本文你将掌握在 Yii 2 应用里统一、安全、可定制地处理错误与异常的全部关键技术。错误处理器能为你做什么Yii 内置的yii\web\ErrorHandler错误处理器把 PHP 原本“粗糙”的错误处理变成了一种愉悦的体验。它主要做了四件事把全部非致命 PHP 错误如 warning、notice转换为可捕获的异常从而让try...catch可以统一拦截它们在调试模式YII_DEBUG true下异常与致命错误会以详细的调用栈call stack和源码行的形式展示支持使用**专用的控制器动作controller action**来展示错误即errorAction机制支持多种错误响应格式HTML、JSON、RAW 等。从源码看yii\web\ErrorHandler继承自抽象基类yii\base\ErrorHandlerframework/base/ErrorHandler.phpWeb 端渲染逻辑实现在 framework/web/ErrorHandler.php 中控制台应用则使用yii\console\ErrorHandlerframework/console/ErrorHandler.php以纯文本方式渲染异常。启用与禁用错误处理器默认是启用的。如果你确实想关闭它可以在应用的入口脚本如web/index.php中把常量YII_ENABLE_ERROR_HANDLER定义为falsedefined(YII_ENABLE_ERROR_HANDLER) or define(YII_ENABLE_ERROR_HANDLER, false);框架在 framework/BaseYii.php 中兜底定义了该常量默认值为truedefined(YII_ENABLE_ERROR_HANDLER) or define(YII_ENABLE_ERROR_HANDLER, true);当该常量保持默认时应用启动流程会调用 framework/base/Application.php 中的registerErrorHandler()把配置好的errorHandler组件实例注册为 PHP 的异常处理器、错误处理器与关闭函数处理器register()方法见 framework/base/ErrorHandler.php。注意关闭该功能会导致 PHP 错误与异常失去统一出口生产环境通常不应这样做。使用错误处理器yii\web\ErrorHandler以应用组件参见 structure-application-components.md的形式注册组件名为errorHandler可通过Yii::$app-errorHandler访问。你可以在应用配置中像下面这样配置它return [ components [ errorHandler [ maxSourceLines 20, ], ], ];上述配置会让异常页面最多展示 20 行源码。需要指出的是源码中 framework/web/ErrorHandler.php 给出的默认值是 19 行$maxSourceLines 19另外还有一个maxTraceSourceLines 13用于控制调用栈中每一帧源码行的展示数量。下面是 Web 版错误处理器常用的可配置属性速查表属性默认值作用maxSourceLines19异常页面主错误位置展示的源码行数上限maxTraceSourceLines13调用栈中每个栈帧展示的源码行数上限errorActionnull无调用栈信息时执行的路由如site/errornull表示由错误处理器自行渲染errorViewyii/views/errorHandler/error.php无调用栈信息时使用的视图exceptionViewyii/views/errorHandler/exception.php含调用栈信息时使用的视图callStackItemViewyii/views/errorHandler/callStackItem.php渲染单个调用栈帧的视图previousExceptionViewyii/views/errorHandler/previousException.php渲染前置异常链的视图displayVars[_GET, _POST, _FILES, _COOKIE, _SESSION]调试页面上展示的 PHP 全局变量列表traceLine{html}调用栈文件行的 HTML 模板此外基类yii\base\ErrorHandler还提供两个值得了解的安全相关属性discardExistingOutput默认true展示错误前丢弃已输出的页面内容避免错误页面混入脏数据memoryReserveSize默认262144即 256KB预分配内存用于在内存耗尽out of memory时仍能渲染错误见 framework/base/ErrorHandler.php。非致命错误也是可捕获的异常正如前文所说错误处理器会把所有非致命 PHP 错误转换为可捕获的异常。这背后的实现是yii\base\ErrorHandler::handleError()framework/base/ErrorHandler.php当错误级别命中当前error_reporting()掩码时它构造一个yii\base\ErrorException并直接throw出去。因此你完全可以用下面的代码来“接住” PHP 错误use Yii; use yii\base\ErrorException; try { 10/0; } catch (ErrorException $e) { Yii::warning(Division by zero.); } // 程序继续执行...需要留意的边界在 PHP 7.4 之前__toString()内不允许抛出异常handleError()对__toString上下文做了特殊处理直接走handleException()并退出同时错误处理器会“手动”加载ErrorException类因为错误可能发生在自动加载本身失效的时刻。这些细节见 framework/base/ErrorHandler.php。抛出 HTTP 异常以呈现规范错误页如果你希望向用户展示“请求无效或异常”的错误页面直接抛出一个yii\web\HttpException即可例如yii\web\NotFoundHttpException。错误处理器会自动设置响应的 HTTP 状态码并使用合适的错误视图展示错误信息use yii\web\NotFoundHttpException; throw new NotFoundHttpException();yii\web\HttpExceptionframework/web/HttpException.php通过$statusCode属性携带标准 HTTP 状态码如 403、404、500并利用Response::$httpStatuses生成用户友好的异常名如 Not Found Exception。实践中常带状态码与消息if ($item null) { // item 不存在 throw new \yii\web\HttpException(404, The requested Item could not be found.); }错误记录日志行为每次异常被处理时基类还会调用logException()framework/base/ErrorHandler.php把异常写入日志普通异常按类名分类HttpException按yii\web\HttpException:{statusCode}分类ErrorException按类名:严重级别分类。这意味着你可以在日志中按状态码检索 404/500 等错误。自定义错误显示错误处理器会根据常量YII_DEBUG的值调整错误展示方式YII_DEBUG true调试模式展示带有详细调用栈和源码行的异常页面便于定位问题YII_DEBUG false只展示错误消息本身避免泄露应用敏感信息如文件路径、SQL 等。Info: 如果异常是yii\base\UserException的子类则无论YII_DEBUG取值如何都不会展示调用栈。因为这类异常被认为是由用户误操作引起的开发者无需修复任何东西。yii\web\HttpException正是UserException的子类所以 404 页面通常不会暴露调用栈。默认情况下错误处理器使用两个内置视图展示错误yii/views/errorHandler/error.php源码见 framework/views/errorHandler/error.php在不展示调用栈时使用。当YII_DEBUG false时这是唯一会展示的错误视图——一个简洁的白底页面仅包含错误名、消息、时间戳等并输出“An internal server error occurred.”之类的通用文案。yii/views/errorHandler/exception.php源码见 framework/views/errorHandler/exception.php在展示调用栈时使用。这是调试模式下的“豪华”页面包含异常类型链接、_GET/_POST/_SESSION等请求全局变量由displayVars控制、每个栈帧的可折叠源码块、复制栈信息按钮等。判断逻辑位于 framework/web/ErrorHandler.php当响应格式为 HTML 且!YII_DEBUG或异常为UserException时走errorView否则走exceptionView。你可以通过配置errorView与exceptionView属性换成自己的视图从而自定义错误展示。不过更推荐的做法是使用下面的errorAction机制。使用错误动作Error Actions自定义错误展示的更好方式是使用专用的错误动作action。首先在配置中把errorHandler组件的errorAction属性指向一个路由return [ components [ errorHandler [ errorAction site/error, ], ] ];errorAction接收一个动作路由。上面的配置表明当错误需要在不展示调用栈的情况下展示时执行site/error动作。从源码看framework/web/ErrorHandler.php此时错误处理器会清空视图、调用Yii::$app-runAction($this-errorAction)并把动作返回值作为响应数据。然后创建SiteController中的error动作namespace app\controllers; use Yii; use yii\web\Controller; class SiteController extends Controller { public function actions() { return [ error [ class yii\web\ErrorAction, ], ]; } }上面的代码使用yii\web\ErrorAction类framework/web/ErrorAction.php定义error动作它会渲染一个名为error的视图views/site/error.php。ErrorAction的关键行为包括init()中通过findException()从Yii::$app-errorHandler-exception获取当前异常如果错误动作被直接访问而没有异常上下文则自动抛出NotFoundHttpException(Page not found.)见 framework/web/ErrorAction.php避免误访问时出现空白页运行时会用setStatusCodeByException()同步响应状态码对 AJAX 请求直接返回错误名: 错误消息的纯文本对普通请求渲染 HTML 视图见 framework/web/ErrorAction.php对UserException用户输入类错误展示真实消息对其他异常则展示defaultMessage默认 An internal server error occurred.防止泄露内部细节见 framework/web/ErrorAction.php。除了使用ErrorAction类你也可以用普通动作方法定义error动作public function actionError() { $exception Yii::$app-errorHandler-exception; if ($exception ! null) { return $this-render(error, [exception $exception]); } }无论采用哪种方式都需要创建视图文件views/site/error.php。如果错误动作定义为yii\web\ErrorAction在该视图文件中你可以使用以下变量name错误名称message错误消息exception异常对象通过它可以获取更多有用信息例如 HTTP 状态码、错误码、错误调用栈等。Info: 如果使用基础项目模板或高级项目模板错误动作和错误视图通常已经定义好了基础模板的SiteController::actions()中就包含error动作视图为views/site/error.php。Note: 如果需要在错误处理器中进行重定向请采用下面的方式Yii::$app-getResponse()-redirect($url)-send(); return;在发送响应后立即return避免后续渲染继续执行。若动作方法返回了Response对象ErrorHandler也会直接把它作为响应结果见 framework/web/ErrorHandler.php。一个可直接落地的错误视图示例下面是views/site/error.php的典型实现同时兼容ErrorAction传入的name/message/exception变量?php /** var string $name 错误名称 */ /** var string $message 错误消息 */ /** var \Throwable $exception 异常对象 */ use yii\helpers\Html; $this-title $name; ? div classsite-error h1? Html::encode($name) ?/h1 div classalert alert-danger ? nl2br(Html::encode($message)) ? /div p 上述错误发生在 Web 服务器处理你的请求时。 如果你认为这是服务器错误请联系我们谢谢。 /p /div自定义错误响应格式错误处理器会根据 response 组件的格式设置来展示错误。如果yii\web\Response::format是html则使用前面提到的 error / exception 视图对于其他格式错误处理器会把异常的数组表示赋值给yii\web\Response::data属性再由响应组件按对应格式转换输出。数组表示由convertExceptionToArray()生成framework/web/ErrorHandler.php包含的字段随模式不同而增减基础字段name异常名、message异常消息、code错误码当异常为HttpException时追加statusHTTP 状态码YII_DEBUG true时追加type异常类名、file、line、stack-trace若为yii\db\Exception还追加error-infoUserException例外不输出栈信息存在前置异常时递归追加previous字段非调试模式下非UserException/HttpException的异常会被替换为通用的 500HttpException消息 An internal server error occurred.防止泄露内部细节。例如当响应格式为json时你会看到类似下面的响应HTTP/1.1 404 Not Found Date: Sun, 02 Mar 2014 05:31:43 GMT Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y Transfer-Encoding: chunked Content-Type: application/json; charsetUTF-8 { name: Not Found Exception, message: The requested resource was not found., code: 0, status: 404 }在 RESTful API 场景下前端通常期望统一包裹的错误结构。你可以通过响应response组件的beforeSend事件来定制错误响应格式在应用配置中注册事件处理器return [ // ... components [ response [ class yii\web\Response, on beforeSend function ($event) { $response $event-sender; if ($response-data ! null) { $response-data [ success $response-isSuccessful, data $response-data, ]; $response-statusCode 200; } }, ], ], ];上面的代码会把错误响应重排成如下格式注意这里把状态码强制改成了 200是否采纳取决于你的 API 约定——通常更推荐保留真实状态码并增加success标记HTTP/1.1 200 OK Date: Sun, 02 Mar 2014 05:31:43 GMT Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y Transfer-Encoding: chunked Content-Type: application/json; charsetUTF-8 { success: false, data: { name: Not Found Exception, message: The requested resource was not found., code: 0, status: 404 } }对于FORMAT_RAW格式错误处理器会直接把异常转换为字符串convertExceptionToString()非调试模式下统一返回 An internal server error occurred.适合纯文本接口。错误处理的内部流程速览综合以上源码一次典型的错误处理流程如下PHP 抛出错误或异常被yii\base\ErrorHandler::register()注册的处理器捕获set_exception_handler/set_error_handler/register_shutdown_function见 framework/base/ErrorHandler.phphandleException()记录当前异常、注销自身以避免递归、预置 500 状态码、写入日志并清空已输出内容见 framework/base/ErrorHandler.phpyii\web\ErrorHandler::renderException()根据响应格式与YII_DEBUG决定出口HTML 且配置了errorAction则运行动作HTML 无动作则渲染errorView或exceptionViewRAW 输出字符串其余格式输出数组见 framework/web/ErrorHandler.php响应组件最终把data按format序列化并发送给客户端致命错误fatal error由handleFatalError()在 shutdown 阶段兜底处理其渲染逻辑与普通异常一致见 framework/base/ErrorHandler.php。值得一提的是yii\console\ErrorHandlerframework/console/ErrorHandler.php对控制台应用如迁移、定时任务做了适配以纯文本输出异常类名、消息与栈帧并递归渲染前置异常便于在终端中阅读。常见问题与最佳实践生产环境务必YII_DEBUG false否则异常页会输出文件路径、源码与请求变量这是敏感信息泄露的主要来源之一。用户输入相关的校验失败用UserException子类既能展示给用户又不会泄露调用栈。业务层统一抛HttpException让错误处理器帮你设置状态码与响应格式而不是在控制器里手写http_response_code()。REST API 统一错误结构通过response组件的beforeSend事件包裹success/data字段或保留状态码 增加success标记让前端错误处理逻辑统一。错误日志联动logException()会自动按HttpException:{statusCode}分类记录配合 runtime-logging.md 中的日志配置可实现对 4xx/5xx 错误的监控与告警。延伸阅读运行时总览一次请求的完整生命周期响应组件与格式设置控制器与动作action、route视图的创建与渲染应用组件配置日志与监控核心实现源码framework/web/ErrorHandler.php、framework/base/ErrorHandler.php、framework/web/ErrorAction.php、framework/views/errorHandler/error.php【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表