PHP異常處理詳解

PHP異常處理詳解

         異常處理(又稱為錯誤處理)功能提供了處理程序運行時出現的錯誤或異常情況的方法。

  異常處理通常是防止未知錯誤產生所采取的處理措施。異常處理的好處是你不用再絞盡腦汁去考慮各種錯誤,這為處理某一類錯誤提供了一個很有效的方法,使編程效率大大提高。當異常被觸發時,通常會發生:
          當前代碼狀態被保存
         代碼執行被切換到預定義的異常處理器函數
          根據情況,處理器也許會從保存的代碼狀態重新開始執行代碼,終止腳本執行,或從代碼中另外的位置繼續執行腳本

          PHP 5 提供了一種新的面向對象的錯誤處理方法??梢允褂脵z測(try)、拋出(throw)和捕獲(catch)異常。即使用try檢測有沒有拋出(throw)異常,若有異常拋出(throw),使用catch捕獲異常。

         一個 try 至少要有一個與之對應的 catch。定義多個 catch 可以捕獲不同的對象。PHP 會按這些 catch 被定義的順序執行,直到完成最后一個為止。而在這些 catch 內,又可以拋出新的異常。

1. 異常的使用

        當一個異常被拋出時,其后的代碼將不會繼續執行,PHP 會嘗試查找匹配的 "catch" 代碼塊。如果一個異常沒有被捕獲,而且又沒用使用set_exception_handler() 作相應的處理的話,那么 PHP 將會產生一個嚴重的錯誤,并且輸出未能捕獲異常(Uncaught Exception … )的提示信息。

     拋出異常,但不去捕獲它:

<?php  
ini_set('display_errors', 'On');  
error_reporting(E_ALL & ~ E_WARNING);  
$error = 'Always throw this error';  
throw new Exception($error);  
// 繼續執行  
echo 'Hello World';  
?>

上面的代碼會獲得類似這樣的一個致命錯誤:

Fatal error: Uncaught exception 'Exception' with message 'Always throw this error' in E:\sngrep\index.php on line 5  
Exception: Always throw this error in E:\sngrep\index.php on line 5  
Call Stack:  
    0.0005     330680   1. {main}() E:\sngrep\index.php:0

2. Try, throw 和 catch

要避免上面這個致命錯誤,可以使用try catch捕獲掉。

處理處理程序應當包括:
         Try – 使用異常的函數應該位于 "try" 代碼塊內。如果沒有觸發異常,則代碼將照常繼續執行。但是如果異常被觸發,會拋出一個異常。
       Throw – 這里規定如何觸發異常。每一個 "throw" 必須對應至少一個 "catch"
       Catch – "catch" 代碼塊會捕獲異常,并創建一個包含異常信息的對象

       拋出異常并捕獲掉,可以繼續執行后面的代碼:

<?php  
try {  
    $error = 'Always throw this error';  
    throw new Exception($error);  
  
    // 從這里開始,tra 代碼塊內的代碼將不會被執行  
    echo 'Never executed';  
  
} catch (Exception $e) {  
    echo 'Caught exception: ',  $e->getMessage(),'<br>';  
}  
  
// 繼續執行  
echo 'Hello World';  
?>

    在 "try" 代碼塊檢測有有沒有拋出“throw”異常,這里拋出了異常。
    "catch" 代碼塊接收到該異常,并創建一個包含異常信息的對象 ($e)。
    通過從這個 exception 對象調用 $e->getMessage(),輸出來自該異常的錯誤消息
    為了遵循“每個 throw 必須對應一個 catch”的原則,可以設置一個頂層的異常處理器來處理漏掉的錯誤。

3. 擴展 PHP 內置的異常處理類

    用戶可以用自定義的異常處理類來擴展 PHP 內置的異常處理類。以下的代碼說明了在內置的異常處理類中,哪些屬性和方法在子類中是可訪問和可繼承的。(注:以下這段代碼只為說明內置異常處理類的結構,它并不是一段有實際意義的可用代碼。)

<?php  
class Exception  
{  
    protected $message = 'Unknown exception';   // 異常信息  
    protected $code = 0;                        // 用戶自定義異常代碼  
    protected $file;                            // 發生異常的文件名  
    protected $line;                            // 發生異常的代碼行號  
  
    function __construct($message = null, $code = 0);  
  
    final function getMessage();                // 返回異常信息  
    final function getCode();                   // 返回異常代碼  
    final function getFile();                   // 返回發生異常的文件名  
    final function getLine();                   // 返回發生異常的代碼行號  
    final function getTrace();                  // backtrace() 數組  
    final function getTraceAsString();          // 已格成化成字符串的 getTrace() 信息  
  
    /* 可重載的方法 */  
    function __toString();                       // 可輸出的字符串  
}

       如果使用自定義的類來擴展內置異常處理類,并且要重新定義構造函數的話,建議同時調用parent::__construct() 來檢查所有的變量是否已被賦值。當對象要輸出字符串的時候,可以重載__toString() 并自定義輸出的樣式。 

     構建自定義異常處理類:

<?php  
  
/** 
 *  
 * 自定義一個異常處理類 
 */  
  
class MyException extends Exception  
{  
    // 重定義構造器使 message 變為必須被指定的屬性  
    public function __construct($message, $code = 0) {  
        // 自定義的代碼  
  
        // 確保所有變量都被正確賦值  
        parent::__construct($message, $code);  
    }  
  
    // 自定義字符串輸出的樣式 */  
    public function __toString() {  
        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";  
    }  
  
    public function customFunction() {  
        echo "A Custom function for this type of exception\n";  
    }  
}  
// 例子 1:拋出自定義異常,但沒有默認的異常  
echo ' 例子 1', '<br>';  
try {  
    // 拋出自定義異常  
    throw new MyException('1 is an invalid parameter', 5);  
} catch (MyException $e) {      // 捕獲異常  
    echo "Caught my exception\n", $e;  
    $e->customFunction();  
} catch (Exception $e) {        // 被忽略  
    echo "Caught Default Exception\n", $e;  
}  
// 執行后續代碼  
// 例子 2: 拋出默認的異常  但沒有自定義異常  
echo '<br>', ' 例子 2:', '<br>';  
try {  
     // 拋出默認的異常    
    throw new Exception('2 isnt allowed as a parameter', 6);  
} catch (MyException $e) {      // 不能匹配異常的種類,被忽略  
    echo "Caught my exception\n", $e;  
    $e->customFunction();  
} catch (Exception $e) {// 捕獲異常  
    echo "Caught Default Exception\n", $e;  
}  
// 執行后續代碼  
// 例子 3: 拋出自定義異常 ,使用默認異常類對象來捕獲  
echo '<br>', ' 例子 3:', '<br>';  
try {  
     // 拋出自定義異常   
    throw new MyException('3 isnt allowed as a parameter', 6);  
} catch (Exception $e) {        // 捕獲異常  
    echo "Default Exception caught\n", $e;  
}  
  
// 執行后續代碼  
// 例子 4  
echo '<br>', ' 例子 4:', '<br>';  
try {  
    echo 'No Exception ';  
} catch (Exception $e) {        // 沒有異常,被忽略  
    echo "Default Exception caught\n", $e;  
}  
  
// 執行后續代碼

           MyException 類是作為舊的 exception 類的一個擴展來創建的。這樣它就繼承了舊類的所有屬性和方法,我們可以使用 exception 類的方法,比如 getLine() 、 getFile() 以及 getMessage()。

4. 嵌套異常處理

    如果在內層 "try" 代碼塊中異常沒有被捕獲,則它將在外層級上查找 catch 代碼塊去捕獲。

try {  
    try {  
    throw new MyException('foo!');  
    } catch (MyException $e) {  
        /* 重新拋出 rethrow it */  
         $e->customFunction();  
        throw $e;  
        
     }  
} catch (Exception $e) {  
        var_dump($e->getMessage());  
}

5. 設置頂層異常處理器 (Top Level Exception Handler)

    set_exception_handler() 函數可設置處理所有未捕獲異常的用戶定義函數。  

<?php  
function myException($exception)  
{  
echo "<b>Exception:</b> " , $exception->getMessage();  
}  
  
set_exception_handler('myException');  
throw new Exception('Uncaught Exception occurred');

6. 異常的規則

  • 需要進行異常處理的代碼應該放入 try 代碼塊內,以便捕獲潛在的異常。

  • 每個 try 或 throw 代碼塊必須至少擁有一個對應的 catch 代碼塊。

  • 使用多個 catch 代碼塊可以捕獲不同種類的異常。

  • 可以在 try 代碼塊內的 catch 代碼塊中再次拋出(re-thrown)異常。

簡而言之:如果拋出了異常,就必須捕獲它,否則程序終止執行。

簡要說明錯誤處理:

1)、使用指定的文件記錄錯誤報告日志

      使用指定的文件記錄錯誤報告日志使用指定的文件記錄錯誤報告日志使用指定的文件記錄錯誤報告日志 如果使用自己指定的文件記錄錯誤日志,一定要確保將這個文件存放在文檔根目錄之外,以減少遭到攻擊的可能。并且該文件一定要讓PHP腳本的執行用戶(Web服務器進程所有者)具有寫權限。假設在Linux操作系統中,將/usr/local/目錄下的error.log文件作為錯誤日志文件,并設置Web服務器進程用戶具有寫的權限。然后在PHP的配置文件中,將error_log指令的值設置為這個錯誤日志文件的絕對路徑。

 

需要將php.ini中的配置指令做如下修改: 

error_reporting  =  E_ALL                   ;將會向PHP報告發生的每個錯誤     
display_errors = Off                        ;不顯示滿足上條 指令所定義規則的所有錯誤報告     
log_errors = On                             ;決定日志語句記錄的位置     
log_errors_max_len = 1024                   ;設置每個日志項的最大長度     
error_log = /usr/local/error.log                ;指定產生的 錯誤報告寫入的日志文件位置

       PHP的配置文件按上面的方式設置完成以后,并重新啟動Web服務器。這樣,在執行PHP的任何腳本文件時,所產生的所有錯誤報告都不會在瀏覽器中顯示,而會記錄在自己指定的錯誤日志/usr/local/error.log中。此外,不僅可以記錄滿足error_reporting所定義規則的所有錯誤,而且還可以使用PHP中的error_log()函數,送出一個用戶自定義的錯誤信息。

該函數的原型如下所示:

      1. bool error_log ( string message [, int message_type  [, string destination [, string extra_headers]]] )  

       此函數會送出錯誤信息到Web服務器的錯誤日志文件、某個TCP服務器或到指定文件中。該函數執行成功則返回TRUE,失敗則返回FALSE。第一個參數message 是必選項,即為要送出的錯誤信息。如果僅使用這一個參數,會按配置文件php.ini中所設置的位置處發送消息。第二個參數message_type為整數值:0表示送到操作系統的日志中;1則使用PHP的Mail()函數,發送信息到某E-mail處,第四個參數extra_headers亦會用到;2則將錯誤信息送到TCP 服務器中,此時第三個參數destination表示目的地IP及Port;3則將信息存到文件destination中。

如果以登入Oracle數據庫出現問題的處理為例,該函數的使用如下所示: 

<?php        
    if(!Ora_Logon($username, $password)){       
          error_log("Oracle數據庫不可用!", 0);        //將錯誤消息寫入到操作系統日志中     
     }     
    if(!($foo=allocate_new_foo()){     
         error_log("出現大麻煩了!", 1, ". mydomain.com");   //發送到管理員郵箱中     
    }    
     error_log("搞砸了!",   2,   "localhost:5000");     //發送到本機對應5000端口的服務器中     
     error_log("搞砸了!",   3,   "/usr/local/errors.log");  //發送到指定的文件中     
?>

      錯誤信息記錄到操作系統的日志里錯誤信息記錄到操作系統的日志里錯誤信息記錄到操作系統的日志里 錯誤報告也可以被記錄到操作系統日志里,但不同的操作系統之間的日志管理有點區別。在Linux上錯誤語句將送往syslog,而在Windows上錯誤將發送到事件日志里。如果你不熟悉syslog,起碼要知道它是基于UNIX的日志工具,它提供了一個API來記錄與系統和應用程序執行有關的消息。Windows事件日志實際上與UNIX的syslog相同,這些日志通??梢酝ㄟ^事件查看器來查看。如果希望將錯誤報告寫到操作系統的日志里,可以在配置文件中將error_log指令的值設置為syslog。

具體需要在php.ini中修改的配置指令如下所示: 

error_reporting  =  E_ALL                   ;將會向PHP報告發生的每個錯誤     
display_errors = Off                            ;不顯示 滿足上條指令所定義規則的所有錯誤報告     
log_errors = On                             ;決定日志語句記錄的位置     
log_errors_max_len = 1024                   ;設置每個日志項的最大長度     
error_log = syslog                          ;指定產生的錯誤報告寫入操作系統的日志里

除了一般的錯誤輸出之外,PHP還允許向系統syslog中發送定制的消息。雖然通過前面介紹的error_log()函數,也可以向syslog中發送定制的消息,但在PHP中為這個特性提供了需要一起使用的4個專用函數。

分別介紹如下: 

define_syslog_variables() 

在使用openlog()、syslog及closelog()三個函數之前必須先調用該函數。因為在調用該函數時,它會根據現在的系統環境為下面三個函數初使用化一些必需的常量。 

openlog() 

打開一個和當前系統中日志器的連接,為向系統插入日志消息做好準備。并將提供的第一個字符串參數插入到每個日志消息中,該函數還需要指定兩個將在日志上下文使用的參數,可以參考官方文檔使用。 

 syslog()

該函數向系統日志中發送一個定制消息。需要兩個必選參數,第一個參數通過指定一個常量定制消息的優先級。例如LOG_WARNING表示一般的警告,LOG_EMERG表示嚴重地可以預示著系統崩潰的問題,一些其他的表示嚴重程度的常量可以參考官方文檔使用。第二個參數則是向系統日志中發送的定制消息,需要提供一個消息字符串,也可以是PHP引擎在運行時提供的錯誤字符串。 

closelog()

該函數在向系統日志中發送完成定制消息以后調用,關閉由openlog()函數打開的日志連接。 

 

如果在配置文件中,已經開啟向syslog發送定制消息的指令,就可以使用前面介紹的四個函數發送一個警告消息到系統日志中,并通過系統中的syslog解析工具,查看和分析由PHP程序發送的定制消息,如下所示:   

<?php  
      define_syslog_variables();     
     openlog("PHP5", LOG_PID , LOG_USER);     
     syslog(LOG_WARNING, "警告報告向syslog中發送的演示, 警告時間:".date("Y/m/d H:i:s"));    
     closelog()

 以Windows系統為例,通過右擊"我的電腦"選擇管理選項,然后到系統工具菜單中,選擇事件查看器,再找到應用程序選項,就可以看到我們自己定制的警告消息了。上面這段代碼將在系統的syslog文件中,生成類似下面的一條信息,是事件的一部分: 

      1. PHP5[3084], 警告報告向syslog中發送的演示, 警告時間:2009/03/26 04:09:11.  

使用指定的文件還是使用syslog記錄錯誤日志,取決于你所在的Web服務器環境。如果你可以控制Web服務器,使用syslog是最理想的,因為你能利用syslog的解析工具來查看和分析日志。但如果你的網站在共享服務器的虛擬主機中運行,就只有使用單獨的文本文件記錄錯誤日志了。

轉自:http://blog.csdn.net/hguisu/article/details/7464977

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2977

(0)
s19930811s19930811
上一篇 2015-06-02
下一篇 2015-06-03

相關推薦

  • bash特性

    一、命令歷史     概念:shell進程能夠將之前執行過的命令保存記錄下來     存儲位置:         1、命令歷史的文件             使用以下環境變量查看相關參數:     &…

    Linux干貨 2016-09-19
  • 計算機的組成及功能

    計算機的組成及功能 計算機由CPU、存儲器、輸入設備、輸出設備等部件組成。 CPU:為計算機的中央處理器,是計算機的核心部分,由運算器和控制器組成。 運算器:CPU中用于進行算術運算和邏輯運算的部件。 控制器:整個CPU的指揮中心,控制程序中指令讀取、解析并產生相應的操作控制信息保證各程序的有序執行。 存儲器:計算機中用來存儲數據、程序等信息的部件,并在需要…

    Linux干貨 2017-07-02
  • Linux下的LVM管理命令

    一. 何為LVM?     Logical Volume Manager的縮寫,它可以把多個分區、硬盤甚至RAID組合成一個存儲設備來使用,并可以擴展或縮減空間。LVM有三層組成組成:底層的PV,中間的VG,上層的LV,如圖所示         &n…

    Linux干貨 2015-12-06
  • Linux 文件系統上的權限

    文件系統上的權限是指文件系統上的文件和目錄的權限,由于Linux是一種多用戶的操作系統,而且允許同一時間登錄多個用戶操作,所以我們就需要一定的管理機制來對限定不同用戶對同一文件或目錄的操作權限。 文件系統上的權限主要針對三類對象(訪問者)進行定義: owner:屬主 u; g 屬組 group; o: 其它 other 每個文件針對每類訪問者都定義了三種權限…

    2017-06-04
  • Linux命令學習總結:cp命令

    Linux命令學習總結:cp命令 命令簡介:      cp命令用來復制文件或目錄。指令英文原義:copy,copy files and directories      指令所在路徑:/bin/cp 命令語法: cp [OPTION]… [-T]   SO…

    Linux干貨 2017-07-23
  • N25_第十周作業

    1、請詳細描述CentOS系統的啟動流程(詳細到每個過程系統做了哪些事情) POST –> BootSequence(BIOS) –>Bootloader(MBR) –>Kernel(ramdisk)–>rootfs(readonly)–>switchroot&#821…

    Linux干貨 2017-02-14
欧美性久久久久