1. 程式人生 > >有關PHP異常和錯誤處理機制的思考(二)

有關PHP異常和錯誤處理機制的思考(二)

通過上篇文章呢,咱們對於PHP異常的定義、捕獲、處理等方面有了簡單的一個概念,這次呢,咱們就來看下關於異常的同胞兄弟,錯誤,以及錯誤的一個處理機制。

我們要知道,PHP中,錯誤處理,比異常處理,更加重要和凸顯價值,咱們之前的文章呢,已經把錯誤的概念介紹過了,現在,咱們就相比較於異常,來給錯誤下個最直觀的定論,那就是,PHP的錯誤就是指會使指令碼執行不正常的情況。

關於PHP的錯誤,它有很多種,包括warning,notice,deprecated,fetal error等,這些和一般意義上的錯誤概念有差別,所以呢,notice不叫通知,而是稱為通知級別的錯誤,同理,warning也不叫警告,它叫警告級別的錯誤,好啦,廢話不多說,我們來看下關於PHP錯誤的幾個類別:

1、deprecated,最低級別的錯誤,表示“不推薦,不建議”

例如,我們在PHP5中使用ereg系列的正則匹配函式就會報此類錯誤,這類錯誤一般是由於使用不推薦,過時的函式或者語法造成的,雖然不影響PHP指令碼的正常執行,但是一般情況下,建議修正。

2、notice,指語法中存在不當的地方

就像我們使用變數,但是沒有定義的話,就會報此類錯誤,最常見的就是陣列索引是字元時,沒有加引號,PHP就把它視作一個常量,先查常量表,找不著再把它視作變數。雖然PHP的指令碼對於語法的要求不是很嚴格,但是仍然建議,對變數進行初始化,這種級別的錯誤,不影響指令碼的正常執行。

3、warning,較高級別錯誤,指語法中出現很不恰當的情況

例如,引數不匹配,就會報此類錯誤,這種級別的錯誤,會導致得不到預期的結果,所以呢,當出現這種級別的錯誤的時候,就需要我們修改程式碼了。

4、fetal error,致命錯誤,導致PHP流程終結,後面程式碼不執行,需修改程式碼

5、prase error,最高級別的語法解析錯誤

上面一到四的錯誤級別都屬於PHP程式碼執行時的錯誤,而語法解析錯誤,就是屬於語法檢查階段的錯誤,這會導致PHP程式碼無法通過語法檢查,而且呢,PHP的錯誤級別不止上述五個,在手冊中,差不多有十六中錯誤級別,上述錯誤只是最常用的。

接下來,我們來通過一段程式碼,來感受下常見的錯誤級別:

$date = "2018-11-27";
if (ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$reg)) {
    echo $reg[3]."-".$reg[2]."-".$reg[1];
}else{
    echo "error for date";
}

if ($i > 5) {
    echo "un be start";
}

$a = array('o' => 1, 2, 3);
echo $a[o];
echo fun();
echo "致命錯誤後,是否還會執行?";
//echo "最高級別的錯誤".$123;

上述程式碼最少演示了四中錯誤級別,以及最後註釋的最高錯誤級別,如果我們看不全的話,可以修改配置檔案裡的error_reporting這個引數的值來檢視全部的錯誤。

說到這個error_reporting了,咱再介紹一個遮蔽錯誤通知的小技巧,第一,在指令碼最頂端加上error_reporting(0)這個東西,就可以遮蔽所有的錯誤通知,完事第二個就是在容易出現錯誤的那一行程式碼的最開始加一個@符號,如@mysqli_connect()。

之後呢,宰門再來看下PHP裡的錯誤處理機制,我們可以使用set_error_handler來接管PHP錯誤處理,也可以使用trigger_error函式,來主動丟擲一個錯誤。

這個set_error_handler函式主要是用來讓使用者自定義錯誤處理函式,需要先建立一個錯誤處理函式,完事設定錯誤級別,語法如下:

set_error_handler(error_function,error_types);

引數描述如下:

  1. error_function,規定發生錯誤時,執行的函式,必需
  2. error_types,規定,在那個錯誤級別,會顯示使用者定義的函式,可選,預設為E_ALL

我們如果使用該函式,系統就會完全繞過PHP內部的標準的PHP錯誤處理函式,如果有必要的話,使用者自己定義的錯誤處理程式,必須要終結(die)指令碼的執行。

還有就是,如果是在指令碼執行前發生錯誤,由於在那個時候自定義的程式還沒有註冊成功,所以就不會用到這個自定義的程式,咱們先來看一個自定義的異常處理函式:

function custonError($errno,$errstr,$errfile,$errline)
{
    echo "錯誤程式碼: 【$errno】 $errstr";
    echo "錯誤所在的程式碼行:$errline,檔案是$errfile";
    echo "PHP的版本 ".PHP_VERSION."(".PHP_OS.")";
}

set_error_handler("custonError",E_ALL|E_STRICT);
$a = array('o' => 1, 2, 3);
echo $a[o];

上述程式碼中的custonError函式,可以對錯誤的詳情,進行格式化的輸出,也可以做任何我們想要做的事情,比如判斷當前的環境和許可權給出不同的錯誤提示,我麼可以使用error_log函式,將錯誤記錄進入log檔案,還可以細緻化處理,針對error的不同,進行相對應的處理,不過,我們自定義的錯誤處理函式一定要有$errno,$errstr,$errfile,$errline這四個輸入變數。

erron它是一組常量,代表錯誤的級別,同時也會有一組整數和其相對應,但是一般使用字串表示,這樣語義會好一點,比如E_WARNING,它的二進位制掩碼就是4.,表示警告資訊。

之後呢,這個custonError函式就會作為回撥引數傳遞給set_error_handler函式,這樣一來,就算是接管了PHP原生的錯誤處理函數了,不過我們要注意的是,這種方式並不能託管所有種類的錯誤,如E_ERROR,E_PRASE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,以及E_STRICT中的部分,上述這些錯誤都會以最原始的方式顯示錯誤資訊,還有可能不顯示錯誤資訊。

set_error_handler函式雖然會接管PHP的錯誤處理,但是,在同一個頁面內,我們可以使用restore_error_handler函式來取消接管,但是注意的是,如果是在接管的狀態下,先前使用@遮蔽錯誤的地方,將會失效,錯誤會顯示。

在PHP的異常中,異常機制的使用是有限的,無法自動丟擲異常,只能手動,並且內建的異常很有限,PHP呢,把許多異常看做錯誤,這樣,我們就可以把這些異常來像錯誤一樣,用set_error_handler函式接管,完事主動丟擲異常,來看下程式碼感受下:

function custonError($errno,$errstr,$errfile,$errline)
{
    throw new Exception("$errno | $errstr", 1);
}

set_error_handler("custonError",E_ALL|E_STRICT);

try {
    $a = 5/0;
} catch (Exception $e) {
    echo "錯誤資訊:".$e->getMessage();
}

類似上述程式碼,我們可以捕獲到異常和非致命的錯誤,在某些方面,彌補了PHP異常處理機制的不足。

這種方式也是有缺陷的,它必須依靠程式設計師自己來掌控對於異常的處理,對於異常的高發區,敏感區,如果程式設計師處理不好,就會導致前面所提到的業務資料不一致問題,優點也是很明顯,就是可以獲取到程式執行時的上下文的資訊,可以進行鍼對性的補救,大概就是這個了。

還有就是這個fetal error錯誤了,它沒有辦法捕獲,也無法在發生這種錯誤之後恢復流程處理,但是我們還是可以使用一些特殊的方法對這些錯誤來進行處理的,這裡就是用到了register_shutdown_function這個函數了,它會在PHP程式終結或者die時會觸發的一個函式,可以給PHP程式一個短時間的迴光返照,在PHP4的時候,類不支援解構函式,所以會用到這函式來模擬實現解構函式的一些作用。

咱們來看段程式碼感受下register_shutdown_function函式的作用:

class shutDown
{
    
    public function stop()
    {
        if (error_get_last()) {
            var_dump(error_get_last());
        }

        die("stop");
    }
}

register_shutdown_function(array(new shutDown(),'stop'));
$a = new a();
echo "you must be death";

我們對於fetal error錯誤還可以做點收尾的工作,但是PHP流程的終止,是必然的。

最後就是對於這個prase error錯誤,咱們就只能看戲了,出了修改PHP的配置檔案,就什麼都做不了,修改內容如下:

log_error=On
error_log=usr/log/php.log

這樣一來,如果出現錯誤,就會被記錄進log檔案中,只是可以方便以後的查詢。

和異常(exception)類似,錯誤處理機制有對應的丟擲錯誤的函式,那就是trigger_error函式,示例程式碼如下:

$a = 0;

if($a==0){
    trigger_error("zero",E_USER_ERROR);
}

echo "stop";

到這裡呢,我們有關於異常和錯誤的介紹和處理也就到此為止了,要注意的是這兩個概念是不同的,正是這種設計,從根本上導致了PHP的異常和其他語言的差異,PHP的異常絕大部分必須通過某種方法手動丟擲,完事才能捕獲到,這是一種半自動化的異常處理機制,總之呢,無論是錯誤還是異常,都可以使用handler系列函式來接管PHP本身的處理機制。

好啦,本次記錄就到這裡了。

如果感覺不錯的話,請多多點贊支援哦。。。