错误处理
Yii 提供了 yiisoft/error-handler 包,使错误处理体验大为改善。Yii 错误处理器具体提供了以下功能:
- 用于捕获未处理错误的 PSR-15 中间件。
- 用于将特定异常映射到自定义响应的 PSR-15 中间件。
- 生产模式和调试模式。
- 调试模式显示详细信息和堆栈跟踪,提供深色和浅色主题,以及无需输入即可搜索错误的快捷按钮。
- 考虑 PHP 配置设置。
- 处理内存溢出错误、致命错误、警告、通知和异常。
- 可使用任何兼容 PSR-3 的日志记录器记录错误。
- 根据请求的 MIME 类型自动检测响应格式。
- 开箱即支持 HTML、纯文本、JSON、XML 和响应头格式的响应。
- 您可以为其他类型实现自定义错误渲染。
本指南介绍如何在 Yii 框架中使用错误处理器。若要了解独立于 Yii 使用的信息,请参阅包说明。
使用错误处理器
错误处理器由两部分组成。第一部分是 Yiisoft\ErrorHandler\Middleware\ErrorCatcher 中间件,注册后可捕获中间件栈执行过程中出现的异常并将其传递给处理器。另一部分是错误处理器本身 Yiisoft\ErrorHandler\ErrorHandler,用于捕获中间件栈之外的异常和致命错误。处理器还会将警告和通知转换为异常,并执行更多便捷操作。
错误处理器在应用程序本身中注册,通常在 ApplicationRunner 中完成。默认情况下,处理器的配置来自容器。您可以在应用配置 config/web.php 中按如下方式配置:
use Psr\Log\LoggerInterface;
use Yiisoft\ErrorHandler\ErrorHandler;
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
return [
// ...
ErrorHandler::class => static function (LoggerInterface $logger, ThrowableRendererInterface $renderer) {
$errorHandler = new ErrorHandler($logger, $renderer);
// Set the size of the reserved memory to 512 KB. Defaults to 256KB.
$errorHandler->memoryReserveSize(524_288);
return $errorHandler;
},
ThrowableRendererInterface::class => static fn () => new HtmlRenderer([
// Defaults to the package file "templates/production.php".
'template' => '/full/path/to/production/template/file',
// Defaults to package file "templates/development.php".
'verboseTemplate' => '/full/path/to/development/template/file',
// Maximum number of source code lines to be displayed. Defaults to 19.
'maxSourceLines' => 20,
// Maximum number of trace source code lines to be displayed. Defaults to 13.
'maxTraceLines' => 5,
// Trace the header line with placeholders (file, line, icon) to be substituted. Defaults to `null`.
'traceHeaderLine' => '<a href="ide://open?file={file}&line={line}">{icon}</a>',
]),
// ...
];如前所述,错误处理器会将所有非致命 PHP 错误转换为可捕获的异常(Yiisoft\ErrorHandler\Exception\ErrorException)。这意味着您可以使用以下代码处理 PHP 错误:
try {
10 / 0;
} catch (\Yiisoft\ErrorHandler\Exception\ErrorException $e) {
// Write a log or something else.
}
// execution continues...该包还有另一个中间件 Yiisoft\ErrorHandler\Middleware\ExceptionResponder,用于将特定异常映射到自定义响应。在应用配置中按如下方式配置:
use Psr\Http\Message\ResponseFactoryInterface;
use Yiisoft\ErrorHandler\Middleware\ExceptionResponder;
use Yiisoft\Injector\Injector;
return [
// ...
ErrorHandler::class => static function (ResponseFactoryInterface $responseFactory, Injector $injector) {
$exceptionMap = [
// Status code with which the factory creates the response.
MyNotFoundException::class => 404,
// PHP callable that must return a `Psr\Http\Message\ResponseInterface`.
MyHttpException::class => static fn (MyHttpException $exception) => new MyResponse($exception),
// ...
],
return new ExceptionResponder($exceptionMap, $responseFactory, $injector);
},
];请注意,在配置应用中间件栈时,必须将 Yiisoft\ErrorHandler\Middleware\ExceptionResponder 放在 Yiisoft\ErrorHandler\Middleware\ErrorCatcher 之前。
渲染错误数据
渲染器可将错误数据渲染为特定格式。以下渲染器开箱即用:
Yiisoft\ErrorHandler\Renderer\HeaderRenderer- 将错误渲染为 HTTP 响应头,用于HEAD请求。Yiisoft\ErrorHandler\Renderer\HtmlRenderer- 将错误渲染为 HTML。Yiisoft\ErrorHandler\Renderer\JsonRenderer- 将错误渲染为 JSON。Yiisoft\ErrorHandler\Renderer\PlainTextRenderer- 将错误渲染为纯文本。Yiisoft\ErrorHandler\Renderer\XmlRenderer- 将错误渲染为 XML。
渲染器根据是否启用调试模式生成详细程度不同的错误数据。
调试模式关闭时,响应头渲染示例:
...
X-Error-Message: An internal server error occurred.
...调试模式开启时,响应头渲染示例:
...
X-Error-Type: Error
X-Error-Message: Call to undefined function App\Controller\myFunc()
X-Error-Code: 0
X-Error-File: /var/www/yii/app/src/Controller/SiteController.php
X-Error-Line: 21
...调试模式关闭时,JSON 渲染输出示例:
{"message":"An internal server error occurred."}调试模式开启时,JSON 渲染输出示例:
{
"type": "Error",
"message": "Call to undefined function App\\Controller\\myFunc()",
"code": 0,
"file": "/var/www/yii/app/src/Controller/SiteController.php",
"line": 21,
"trace": [
{
"function": "index",
"class": "App\\Controller\\SiteController",
"type": "->"
},
{
"file": "/var/www/yii/app/vendor/yiisoft/injector/src/Injector.php",
"line": 63,
"function": "invokeArgs",
"class": "ReflectionFunction",
"type": "->"
},
...
]
}调试模式关闭时,HTML 渲染示例:

调试模式开启且使用浅色主题时,HTML 渲染示例:

调试模式开启且使用深色主题时,HTML 渲染示例:

错误捕获器根据 accept HTTP 请求头决定如何渲染异常。若为 text/html 或未知内容类型,将使用错误或异常 HTML 模板显示错误。对于其他 MIME 类型,错误处理器会选择您在错误捕获器中注册的不同渲染器。默认支持 JSON、XML 和纯文本。
实现自定义渲染器
在注册错误捕获器中间件时,可以通过提供自己的 Yiisoft\ErrorHandler\ThrowableRendererInterface 实例来自定义错误响应格式。
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\ErrorHandler\ErrorData;
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
final readonly class MyRenderer implements ThrowableRendererInterface
{
public function render(Throwable $t, ServerRequestInterface $request = null): ErrorData
{
return new ErrorData($t->getMessage());
}
public function renderVerbose(Throwable $t, ServerRequestInterface $request = null): ErrorData
{
return new ErrorData(
$t->getMessage(),
['X-Custom-Header' => 'value-header'], // Headers to be added to the response.
);
}
};可以在应用配置 config/web.php 中进行配置:
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Yiisoft\ErrorHandler\ErrorHandler;
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
return [
// ...
ErrorCatcher::class => static function (ContainerInterface $container): ErrorCatcher {
$errorCatcher = new ErrorCatcher(
$container->get(ResponseFactoryInterface::class),
$container->get(ErrorHandler::class),
$container,
);
// Returns a new instance without renderers by the specified content types.
$errorCatcher = $errorCatcher->withoutRenderers('application/xml', 'text/xml');
// Returns a new instance with the specified content type and renderer class.
return $errorCatcher->withRenderer('my/format', new MyRenderer());
},
// ...
];友好异常
Yii 错误渲染器支持友好异常,让团队的错误处理体验更佳。其理念是为问题提供可读的名称和可能的解决方案:
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
final readonly class RequestTimeoutException extends \RuntimeException implements FriendlyExceptionInterface
{
public function getName(): string
{
return 'Request timed out';
}
public function getSolution(): ?string
{
return <<<'SOLUTION'
Likely it is a result of resource request is not responding in a timely fashion. Try increasing timeout.
SOLUTION;
}
}当应用程序抛出此类异常时,若调试模式已开启,错误渲染器将显示名称和解决方案。