Yii中的錯誤及異常處理
Yii已經默認已經在CApplication上實現了異常和錯誤的接管,這是通過php的set_exception_handler, set_error_handler實現的。通過這兩個PHP內置函數,可以對程序中未捕獲的異常以及錯誤進行接管處理,從而提高程序的可維護性。這在大型系統是至關重要的,當發生錯誤時,我們希望能將相關詳細信息記錄,甚至是即時發送報警,從而縮短故障修復時間,提高整個系統的穩定性。
默認情況下,Yii會將異常處理分配給CApplication::handleException, 將錯誤處理分配給CApplication::handleError,但是可以通過在入口文件中定義YII_ENABLE_EXCEPTION_HANDLER, YII_ENABLE_ERROR_HANDLER兩個常量為false禁止使用Yii的異常和錯誤接管機制。
以下內容中,將異常和錯誤統稱為錯誤,如有必要會進行詳細區分說明。
YII_DEBUG常量(默認為false, 可以在入口文件中設置)對錯誤信息的顯示有很重要的影響,debug模式下,錯誤的輸出是最詳細的。而程序一旦投入運行,則應將YII_DEBUG修改為false。
無論是否處于debug模式,Yii程序產生錯誤時均會將相關錯誤信息進行記錄(錯誤級別為error, 分類默認為application)。不同之處是debug模式時會直接在web頁上顯示詳細信息。
CApplication:: handleError($code,$message,$file,$line)
上面的方法實現了相關邏。特別注意restore_error_handler,restore_exception_handler兩個函數,如果沒有這兩個函數的調用,那么在后續的錯誤處理過程中,當再次產生異常或是錯誤時,又會調用CApplication:: handleError ,從而可能造成死循環,故Yii在此處臨時禁止了使用CApplication:: handleError 接管后續的錯誤和異常(使用php默認的錯誤處理機制),這就保證了不會因之產生循環調用。
PHP錯誤的處理
當產生錯誤時,PHP會在日志中記錄哪些信息?
錯誤代碼(即PHP的E_ERROR E_WARNING E_STRICT E_DEPRECATED)
消息內容(如 Undefined vaiable $input)
產生錯誤的文件路徑
產生錯誤的行號
額外的跟蹤回溯信息(這是通過debug_backtrace實現的)
當前URL
除了記錄相應日志之外,Yii還會對錯誤進行后續處理(如中斷運行、顯示錯誤頁等),默認情況下錯誤的處理會交給CErrorHandler組件處理(但可以通過給CApplicaton綁定onError事件處理器而實現錯誤處理的二次接管,此處的設計很靈活!)。
此時將產生一個CErrorEvent(并包含$code,$message,$file,$line幾項關鍵參數),傳遞給CErrorHandler組件進行處理。具體是交給CErrorHandler::handleError處理之。這個流程主要是將錯誤相關信息進行整理,并以合適的方式進行顯示。
是否為debug模式(YII_DEBUG==true),對錯誤信息的顯示結果有極大影響。調試模式下我們希望能顯示詳細的錯誤跟蹤信息,而在生產模式下,我們希望給用戶顯示友好的頁面。所以,此處的錯誤顯示有所不同,下面區分說明之。
當處于調試模式時,將直接渲染exception視圖展示錯誤。將按以下路徑搜索:
protected/views/system/exception.php
YII_PATH/views/exception.php
顯然,默認情況下并沒有在應用程序中定義views/system目錄,故會使用系統框架自帶的視圖文件。最終包含的文件將是Yii框架中的views/exception.php。
從以上分析中可以得知,在調試模式下如果我們要使用自定義異常頁面(一般這么做的意義可能不大),則需要配置文件protected/views/system/exception.php, 可使用的變量即$data。
當處于非調試模式下時,會作如下處理:
- 配置文件中若為errorHandler組件定義了errorAction路由信息,則直接運行之,否則執行第2步流程。
- 嘗試加載error視圖,按以下路徑搜索(第一個搜索到的文件將被使用)
protected/views/system/zh_cn/error500.php
protected/views/system/error500.php
protected/views/system/zh_cn/error.php
protected/views/system/error.php
YII_PATH/views/zh_cn/error500.php
YII_PATH/views/error500.php
YII_PATH/views/zh_cn/error.php
Y II_PATH/views/error.php
異常的處理
根據前面的分析,異常的處理機制與錯誤處理機制類似,也會記錄日志,級別是error, 分類為"exception.$EXCEPTIONCLASS", 若是CHttpException類異常,分類名稱則為exception.CHttpException.$STATUS_CODE。如數據的異常分類稱為exception.CDbException。
接下來將錯誤事件CExceptionEvent交由errorHandler處理,所有錯誤信息都由CExceptionEvent對象傳遞而來。處理方法如下:
- 如果是調試模式,則按以下順序搜索視圖文件,第一個搜索到的文件將被使用
protected/views/system/exception.php
YII_PATH/views/exception.php
- 如果是非調試模式,并在配置文件中為errorHandler組件定義了errorAction屬性路由,則運行之,否則進入第3步。
- 按以下順序嘗試加載視圖文件,第一個搜索到的文件將被使用
- protected/views/system/zh_cn/error500.php
protected/views/system/error500.php
protected/views/system/zh_cn/error.php
protected/views/system/error.php
YII_PATH/views/zh_cn/error500.php
YII_PATH/views/error500.php
YII_PATH/views/zh_cn/error.php
Y II_PATH/views/error.php
使用流程圖描述,會更清楚一些:
搜尋視圖文件流程比較重要,因為它關系到我們如何自定義錯誤頁面的細節問題,后續的流程圖詳細描述其過程。
從圖中可以看出,最容易的方式還是給errorHandler組件設置errorAction屬性指定錯誤發生的路由
一般而言,我們最關心的是生產模式下錯誤頁面的顯示問題,經過以上分析,有兩種方法可用:
- 配置文件中為errorHandler組件定義errorAction路由屬性(應該優先使用這個方式,以達到靈活配置目的)
- 定義以下文件中的任意一個,實現自定義錯誤頁(不推薦)
Protected/views/system/zh_cn/error500.php
protected/views/system/error500.php
protected/views/system/zh_cn/error.php
protected/views/system/error.php
第1種方式靈活可控,可以在控制器中指定視圖文件,靈活可控。