Laravel Exception處理邏輯解析
vendor/laravel/framework/src/Illuminate/Foundation/Application.php
- app首先繼承了container,作為一個容器類存在
- 注冊了laravel運行過程的需要的基礎類進容器,並且生成了運行需要的實例。承擔了初始化功能。這里需要額外說一下,app里面說的所謂注冊,不是指綁定,應該是直接直接實例化了,並注入到容器。但是,針對provider,實例化了provider,並運行了,並不會生成實際的類,而是將類綁定。
ExceptionHandler的注冊就是在Application的__construct方法中。
$this->registerErrorHandling();
接着我們來到定義該方法的trait:RegistersExceptionHandlers找到該方法。看一下該方法實現了什么樣的邏輯。便於理解我加上了一些注釋。
protected function registerErrorHandling()
{
error_reporting(-1);//-1報告所有異常,包括后續新定義的異常級別,作用與E_ALL相同
/** set_error_handler,set_exception_handler,register_shutdown_function分別注冊不同級別不同類型異常的處理方法。 */
set_error_handler(function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
});//代替標准錯誤處理方法
set_exception_handler(function ($e) {
$this->handleUncaughtException($e);
});//兜底異常處理方法注冊
register_shutdown_function(function () {
$this->handleShutdown();
});//注冊一個在腳本正常或非正常情況終止執行時調用的方法。(終極兜底)
}
我們看到laravel主要通過三個原生方法來實現主要的ExceptionHandler機制。下面我們分開看一下這里都分別注冊了哪些函數。
set_error_handler
function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
這塊很簡單,set_error_handler注冊的函數會代替原生的報錯處理邏輯。這里可以看到laravel將執行error作為異常拋出。並且保留了error的主要信息。(level,msg,file,line)
set_exception_handler
/**set_exception_handler(function ($e) {
$this->handleUncaughtException($e);
});**/
protected function handleUncaughtException($e)
{
//從容器中實例化一個真正的handler。(使用make方法)
$handler = $this->resolveExceptionHandler();
//如果獲取到的是Error,通過Error信息實例化一個已定義的“可拋出的致命錯誤”
if ($e instanceof Error) {
$e = new FatalThrowableError($e);
}
//記錄日志(先判斷是否報告)
$handler->report($e);
//render錯誤
if ($this->runningInConsole()) {
$handler->renderForConsole(new ConsoleOutput, $e);
} else {
$handler->render($this->make('request'), $e)->send();
}
}
- resolveExceptionHandler方法從容器中make了一個handler實例,從該方法可以找到laravel實現的handler方法。該方法會判斷是否綁定抽象類型來判斷使用開發者自行綁定的ExceptionHandler還是系統自帶的。handler的作用只有一點,就是解析異常,並將異常處理成我們想要的,更加用戶友好的方式展示。(++render方法,可以看“Laravel\Lumen\Exceptions\Handler”,該類實現了Laravel的ExceptionHandler接口,想要自定義異常輸出的話也可以參考該類++)
- report和render方法都是handler中定義的,report會先進行判斷,並根據判斷結果決定是否記錄log。render自然不必多說,formatexception info,並作為一個HTTP response輸出。
register_shutdown_function
protected function handleShutdown()
{
if (! is_null($error = error_get_last()) && $this->isFatalError($error['type'])) {
$this->handleUncaughtException(new FatalErrorException(
$error['message'], $error['type'], 0, $error['file'], $error['line']
));
}
}
error_get_last()是5.2版本實現的方法,能夠很好的配合的register_shutdown_function進行兜底處理。改善了需要自定義變量判斷的方法。
error_get_last 返回了一個關聯數組,描述了最后錯誤的信息,以該錯誤的 "type"、 "message"、"file" 和 "line" 為數組的鍵。
另外,該方法規定只有致命的錯誤才會啟動。