yii2.0框架的錯誤和異常處理機制


 

 

FROM : http://tech.lubanr.com/2015/12/12/yii2-0框架的錯誤和異常處理機制/

在應用開發中,錯誤和異常處理機制是一塊比較重要的模塊。yii框架有專門的模塊來進行錯誤和異常處理,本文嘗試從yii2.0的源碼出發,對yii框架的錯誤和異常處理機制做一個說明。

yii2.0中,錯誤和異常處理最先接觸到的就是 frontend/config/main.php 中的 component中的一項配置 
'errorHandler' => ['errorAction'=>'site/error']

我們先記下這個配置,然后來看看yii框架的啟動過程以及錯誤和異常處理在yii框架中的注冊過程。

yii框架的入口頁面只有一個: frontend/web/index.php, 所有的訪問都會經過nginx重寫到這個腳本上(靜態文件除外)。

該文件的內容如下:

defined('YII_DEBUG') or define('YII_DEBUG', true);

defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../../vendor/autoload.php');

require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');

require(__DIR__ . '/../../common/config/bootstrap.php');

require(__DIR__ . '/../config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(

require(__DIR__ . '/../../common/config/main.php'),

require(__DIR__ . '/../../common/config/main-local.php'),

require(__DIR__ . '/../config/main.php'),

require(__DIR__ . '/../config/main-local.php')

);

$application = new yii\web\Application($config);

$application->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() 

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']); 
}

該方法主要做了兩個動作 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

$msg = "An Error occurred while handling another error:\n";

$msg .= (string) $e;

$msg .= "\nPrevious exception:\n";

$msg .= (string) $exception;

if (YII_DEBUG) {

if (PHP_SAPI === 'cli') {

echo $msg . "\n";

} else {

echo '

' . htmlspecialchars($msg, ENT_QUOTES, Yii::$app->charset) . '

';

}

} else {

echo 'An internal server error occurred.';

}

$msg .= "\n\$_SERVER = " . VarDumper::export($_SERVER);

error_log($msg);

if (defined('HHVM_VERSION')) {

flush();

}

exit(1);

}

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

$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;

}

} elseif ($response->format === Response::FORMAT_HTML) {

if (YII_ENV_TEST || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {

// AJAX request

$response->data = '

' . $this->htmlEncode($this->convertExceptionToString($exception)) . '

';

} 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 = $exception;

} else {

$response->data = $this->convertExceptionToArray($exception);

}

if ($exception instanceof HttpException) {

$response->setStatusCode($exception->statusCode);

} else {

$response->setStatusCode(500);

}

$response->send(); 
}

已經不需要再解釋什么了,這個就是大家經常看到的異常出錯頁面的渲染處理過程,如果我們需要定制自己的異常處理方式,需要做的就是繼承yii\base\ErrorHandler,寫一個定制的renderException,最后在$config中定制自己的errorHandler, 如:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM