Yii2 之错误处理深入分析

来源:转载



## ▪ 前言
在 Yii2 使用中,我们发现一但程序出现错误,Yii2 就能自动显示其专用的错误提示界面,和我们写原生态时出现的错误提示界面完全不一样。它究竟是怎么做到的呢:是在哪里设置监听的?亦或在哪里用的try catch?”。
其实 PHP 有自己专用的错误处理 API,当程序出现问题时,可以自动调用指定函数。而 Yii2 正是利用这一点,在其启动的时候,使用 PHP 内置的 `set_error_handler` 将自己的错误处理注册进步并关闭 PHP 自身的错误显示。
## ▪ Yii2 错误自定义处理
在 [官方教程](http://www.yiichina.com/doc/guide/2.0/runtime-handling-errors) 中,它告诉我们要开启自定义的错误,需要进行如下配置组件:
```
return [
// ...'components' => [
// ...

'errorHandler' => [
'errorAction' => 'site/error',
],
]// ...
];
```
> `'errorAction' => 'site/error'` 表示一但发生错误,那么 Yii2 将调用 `SiteController` 控制器的 `errorAction` 方法,该方法是你自己自定义显示错误的入口。
实际开发中很多新手发现虽然填写了这个配置,但是发现 Yii2 并没有如我们所想的进入到 `SiteController` 控制器的 `errorAction` 方法,究竟是什么原因?看下面我们一步一步来解释。
## ▪ Yii2 错误注册机制
##### X. 预定义开启错误处理常量
下面的变量将控制是否开启错误处理,默认为开启,你可以通过设置该变量值关闭错误日志
```
# /yii/BaseYii.php
/**
* This constant defines whether error handling should be enabled. Defaults to true.
*/
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
```
##### X. 预定义默认组件 errorHandler
下面的函数主要是设置错误处理的默认类,如果你的配置文件中没有设置 `errorHandler` 的 `class`,那么将直接使用 `'yii/web/ErrorHandler'` 类对象进行处理:
```
# yii2/web/Application.php
/**
* @inheritdoc
*/
public function coreComponents()
{
return array_merge(parent::coreComponents(), [
'request' => ['class' => 'yii/web/Request'],
'response' => ['class' => 'yii/web/Response'],
'session' => ['class' => 'yii/web/Session'],
'user' => ['class' => 'yii/web/User'],
'errorHandler' => ['class' => 'yii/web/ErrorHandler'],
]);
}
```
##### X. 运行时初始化注册错误处理机制流程
应用入口文件 `index.php` 初始化时将调用基类的 `yii/base/Application.php` 构造函数,其中包含了开始注册错误处理机制:
```
# index.php
// ...
(new yii/web/Application($config))->run();
```
```
# yii/base/Application.php
public function __construct($config = [])
{
Yii::$app = $this;
$this->setInstance($this);
$this->state = self::STATE_BEGIN;
$this->preInit($config);
// 开始注册错误处理机制
$this->registerErrorHandler($config);
Component::__construct($config);
}
// ...
/**
* 注册错误处理组件
* @param array $config application config
*/
protected function registerErrorHandler(&$config)
{
if (YII_ENABLE_ERROR_HANDLER) {
// 获取错误处理组件信息
if (!isset($config['components']['errorHandler']['class'])) {
echo "Error: no errorHandler component is configured./n";
exit(1);
}
$this->set('errorHandler', $config['components']['errorHandler']);
unset($config['components']['errorHandler']);

// 获取错误处理对象并注册错误处理钩子
$this->getErrorHandler()->register();
}
}
```
上述的 `$this->getErrorHandler()->register();` 将调用如下代码:
```
# yii/base/ErrorHandler
/**
* Register this error handler
*/
public function register()
{
// 关闭 PHP 原始错误提示
ini_set('display_errors', false);// 代码的 $this 表示 yii/base/ErrorHandler 的类对象
// 实际情况 Yii2 是构建了 yii/web/ErrorHandler 的类对象,其继承自 yii/base/ErrorHandler 类// 注册异常的处理钩子
set_exception_handler([$this, 'handleException']);// 注册错误的处理钩子
if (defined('HHVM_VERSION')) {
set_error_handler([$this, 'handleHhvmError']);
} else {
set_error_handler([$this, 'handleError']);
}if ($this->memoryReserveSize > 0) {
$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}register_shutdown_function([$this, 'handleFatalError']);
}
```
> 由于组件里默认 `errorHandler` 的处理类是 `yii/web/ErrorHandler`,所以上述代码 `set_exception_handler([$this, 'handleException']);` 表示调用 `yii/web/ErrorHandler` 类对象的 `handleException` 方法,同理 `set_error_handler([$this, 'handleError']);` 将类对象调用 `handleError` 来显示错误
通过上面的方法,我们能看到,Yii2 通过 PHP 异常处理函数 `set_exception_handler` 设置处理异常的方法,通过错误处理函数 `set_error_handler` 设置了处理错误的方法。当有代码中有异常或者错误设置的时候,如果上层没有进一步的异常处理机制,就会被整个全局函数捕捉,并加以处理。## ▪ Yii2 错误核心处理方法
在 **Yii2 错误自定义处理** 中我们了解了 Yii2 错误注册的原理并知道错误的发生后,Yii2 将调用 `yii/web/ErrorHandler` 类对象的 `handleException` 和 `handleError` 方法。
其实 `yii/web/ErrorHandler` 类中并没有 `handleException` 和 `handleError` 方法,但是在其基类 `yii/base/ErrorHandler` 有,这两个函数最终都将调用 `yii/web/ErrorHandler` 的 `renderException` 方法,代码如下:
```
# yii/web/ErrorHandler
/**
* Renders the exception.
* @param /Exception $exception the exception to be rendered.
*/
protected function renderException($exception)
{
// ...
// 注意:控制错误是否能自定义的关键
// !YII_DEBUG 表示了如果你现在是在 Debug 模式下,那么不能自定义错误
// 即使你配置了组件的 'errorHandler' => ['errorAction' => 'site/error'] 参数
$useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);
// 传递到自定义错误处理方法
if ($useErrorView && $this->errorAction !== null) {
$result = Yii::$app->runAction($this->errorAction);
if ($result instanceof Response) {
$response = $result;
} else {
$response->data = $result;
}
}// ...
}
```
在最终的错误显示方法 `renderException` 我们可以看出:**自定义的错误需要在非 DEBUG 模式下才能生效**## ▪ Yii2 自定义错误使用
自定义错误的使用详见 [官方教程](http://www.yiichina.com/doc/guide/2.0/runtime-handling-errors) 的 [自定义错误显示](http://www.yiichina.com/doc/guide/2.0/runtime-handling-errors#customizing-error-display)。

分享给朋友:
您可能感兴趣的文章:
随机阅读: