yii2.0框架的错误和异常处理机制 --- 源码解读

在应用开发中,错误和异常处理机制是一块比较重要的模块。yii框架有专门的模块来进行错误和异常处理,本文尝试从yii2.0的源码出发,对yii框架的错误和异常处理机制做一个说明。

yii2.0中,错误和异常处理最先接触到的就是 frontend/config/main.php 中的 component中的一项配置 :

'errorHandler' => ['errorAction'=>'site/error']

我们先记下这个配置,然后来看看yii框架的启动过程以及错误和异常处理在yii框架中的注册过程。

yii框架的入口页面只有一个: /web/index.php, 所有的访问都会经过nginx重写到这个脚本上(静态文件除外)。

该文件的内容如下:

// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';

$config = require __DIR__ . '/../config/web.php';

(new yii\web\Application($config))->run();

可以看出,$application是核心所在,我们来看看$application都干了些什么:

(yii\base\Application 195行)

yii\web\Application在初始化的时候,会调用基类 yii\base\Application的init函数,在init中,有这样一行代码:(第204行)

$this->registerErrorHandler($config);

该行的作用是在application中注册error和exception处理。 

    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();
        }
    }

registerErrorHandler方法的定义如上。

$config中的components[‘errorHandler’][‘class’]在web\Application中的coreComponents变量定义并在preInit()函数中被merge到config中来,此处$config[‘components’][‘errorHandler’][‘class’] = yii\web\ErrorHandler,

因此registerErrorHandler($config) 最终的结果是执行了ErrorHandler类中的register()方法。

yii\web\ErrorHandler的register方法代码:(在yii\base\ErrorHandler中定义) 

    public function register()
    {
        if (!$this->_registered) {
            ini_set('display_errors', false);
            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']);
            $this->_registered = true;
        }
    }

该方法主要做了两个动作 set_exception_handler和set_error_handler.

我们先值考虑异常处理:异常处理由ErrorHandler实例的HandleException方法处理。 

  public function handleException($exception)
    {
        if ($exception instanceof ExitException) {
            return;
        }

        $this->exception = $exception;

        // disable error capturing to avoid recursive errors while handling exceptions
        $this->unregister();

        // set preventive HTTP status code to 500 in case error handling somehow fails and headers are sent
        // HTTP exceptions will override this value in renderException()
        if (PHP_SAPI !== 'cli') {
            http_response_code(500);
        }

        try {
            $this->logException($exception);
            if ($this->discardExistingOutput) {
                $this->clearOutput();
            }
            $this->renderException($exception);
            if (!YII_ENV_TEST) {
                \Yii::getLogger()->flush(true);
                if (defined('HHVM_VERSION')) {
                    flush();
                }
                exit(1);
            }
        } catch (\Exception $e) {
            // an other exception could be thrown while displaying the exception
            $this->handleFallbackExceptionMessage($e, $exception);
        } catch (\Throwable $e) {
            // additional check for \Throwable introduced in PHP 7
            $this->handleFallbackExceptionMessage($e, $exception);
        }

        $this->exception = null;
    }

该方法处理完之后,程序退出,可以看到 renderException决定了最终异常的展现形式。renderException是一个纯虚函数,yii\web\ErrorHandler里面有默认实现: 

   protected function renderException($exception)
    {
        if (Yii::$app->has('response')) {
            $response = Yii::$app->getResponse();
            // reset parameters of response to avoid interference with partially created response data
            // in case the error occurred while sending the response.
            $response->isSent = false;
            $response->stream = null;
            $response->data = null;
            $response->content = null;
        } else {
            $response = new Response();
        }

        $response->setStatusCodeByException($exception);

        $useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);

        if ($useErrorView && $this->errorAction !== null) {
            Yii::$app->view->clear();
            $result = Yii::$app->runAction($this->errorAction);
            if ($result instanceof Response) {
                $response = $result;
            } else {
                $response->data = $result;
            }
        } elseif ($response->format === Response::FORMAT_HTML) {
            if ($this->shouldRenderSimpleHtml()) {
                // AJAX request
                $response->data = '<pre>' . $this->htmlEncode(static::convertExceptionToString($exception)) . '</pre>';
            } else {
                // if there is an error during error rendering it's useful to
                // display PHP error in debug mode instead of a blank screen
                if (YII_DEBUG) {
                    ini_set('display_errors', 1);
                }
                $file = $useErrorView ? $this->errorView : $this->exceptionView;
                $response->data = $this->renderFile($file, [
                    'exception' => $exception,
                ]);
            }
        } elseif ($response->format === Response::FORMAT_RAW) {
            $response->data = static::convertExceptionToString($exception);
        } else {
            $response->data = $this->convertExceptionToArray($exception);
        }

        $response->send();
    }

已经不需要再解释什么了,这个就是大家经常看到的异常出错页面的渲染处理过程。

如果我们需要定制自己的异常处理方式,需要做的就是:

1、继承yii\base\ErrorHandler,写一个定制的renderException

2、最后在$config中定制自己的errorHandler, 如:

‘errorHandler’=>[
     ‘action’=>’site/error’,
     ‘class’=>’common\web\ErrorHandler’
]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值