转载 PHP的错误和异常处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了转载 PHP的错误和异常处理相关的知识,希望对你有一定的参考价值。

PHP异常处理详解

         异常处理(又称为错误处理)功能提供了处理程序运行时出现的错误或异常情况的方法。

  异常处理通常是防止未知错误产生所采取的处理措施。异常处理的好处是你不用再绞尽脑汁去考虑各种错误,这为处理某一类错误提供了一个很有效的方法,使编程效率大大提高。当异常被触发时,通常会发生:
          当前代码状态被保存
         代码执行被切换到预定义的异常处理器函数
          根据情况,处理器也许会从保存的代码状态重新开始执行代码,终止脚本执行,或从代码中另外的位置继续执行脚本

          php 5 提供了一种新的面向对象的错误处理方法。可以使用检测(try)、抛出(throw)和捕获(catch)异常。即使用try检测有没有抛出(throw)异常,若有异常抛出(throw),使用catch捕获异常。

         一个 try 至少要有一个与之对应的 catch。定义多个 catch 可以捕获不同的对象。PHP 会按这些 catch 被定义的顺序执行,直到完成最后一个为止。而在这些 catch 内,又可以抛出新的异常。

1. 异常的使用

 

        当一个异常被抛出时,其后的代码将不会继续执行,PHP 会尝试查找匹配的 "catch" 代码块。如果一个异常没有被捕获,而且又没用使用set_exception_handler() 作相应的处理的话,那么 PHP 将会产生一个严重的错误,并且输出未能捕获异常(Uncaught Exception ... )的提示信息。

     抛出异常,但不去捕获它:

 

[php] view plain copy
 
 print?
  1. <?php  
  2. ini_set(‘display_errors‘, ‘On‘);  
  3. error_reporting(E_ALL & ~ E_WARNING);  
  4. $error = ‘Always throw this error‘;  
  5. throw new Exception($error);  
  6. // 继续执行  
  7. echo ‘Hello World‘;  
  8. ?>   

上面的代码会获得类似这样的一个致命错误:

 

 

[plain] view plain copy
 
 print?
  1. Fatal error: Uncaught exception ‘Exception‘ with message ‘Always throw this error‘ in E:\sngrep\index.php on line 5  
  2. Exception: Always throw this error in E:\sngrep\index.php on line 5  
  3. Call Stack:  
  4.     0.0005     330680   1. {main}() E:\sngrep\index.php:0  

 

2. Try, throw 和 catch

 
要避免上面这个致命错误,可以使用try catch捕获掉。

处理处理程序应当包括:
         Try - 使用异常的函数应该位于 "try" 代码块内。如果没有触发异常,则代码将照常继续执行。但是如果异常被触发,会抛出一个异常。
       Throw - 这里规定如何触发异常。每一个 "throw" 必须对应至少一个 "catch"
       Catch - "catch" 代码块会捕获异常,并创建一个包含异常信息的对象

 

       抛出异常并捕获掉,可以继续执行后面的代码:

[php] view plain copy
 
 print?
  1. <?php  
  2. try {  
  3.     $error = ‘Always throw this error‘;  
  4.     throw new Exception($error);  
  5.   
  6.     // 从这里开始,tra 代码块内的代码将不会被执行  
  7.     echo ‘Never executed‘;  
  8.   
  9. } catch (Exception $e) {  
  10.     echo ‘Caught exception: ‘,  $e->getMessage(),‘<br>‘;  
  11. }  
  12.   
  13. // 继续执行  
  14. echo ‘Hello World‘;  
  15. ?>   

 


    在 "try" 代码块检测有有没有抛出“throw”异常,这里抛出了异常。
    "catch" 代码块接收到该异常,并创建一个包含异常信息的对象 ($e)。
    通过从这个 exception 对象调用 $e->getMessage(),输出来自该异常的错误消息
    为了遵循“每个 throw 必须对应一个 catch”的原则,可以设置一个顶层的异常处理器来处理漏掉的错误。

 

3. 扩展 PHP 内置的异常处理类

 

    用户可以用自定义的异常处理类来扩展 PHP 内置的异常处理类。以下的代码说明了在内置的异常处理类中,哪些属性和方法在子类中是可访问和可继承的。(注:以下这段代码只为说明内置异常处理类的结构,它并不是一段有实际意义的可用代码。)

 

[php] view plain copy
 
 print?
  1. <?php  
  2. class Exception  
  3. {  
  4.     protected $message = ‘Unknown exception‘;   // 异常信息  
  5.     protected $code = 0;                        // 用户自定义异常代码  
  6.     protected $file;                            // 发生异常的文件名  
  7.     protected $line;                            // 发生异常的代码行号  
  8.   
  9.     function __construct($message = null, $code = 0);  
  10.   
  11.     final function getMessage();                // 返回异常信息  
  12.     final function getCode();                   // 返回异常代码  
  13.     final function getFile();                   // 返回发生异常的文件名  
  14.     final function getLine();                   // 返回发生异常的代码行号  
  15.     final function getTrace();                  // backtrace() 数组  
  16.     final function getTraceAsString();          // 已格成化成字符串的 getTrace() 信息  
  17.   
  18.     /* 可重载的方法 */  
  19.     function __toString();                       // 可输出的字符串  
  20. }  

 

       如果使用自定义的类来扩展内置异常处理类,并且要重新定义构造函数的话,建议同时调用parent::__construct() 来检查所有的变量是否已被赋值。当对象要输出字符串的时候,可以重载__toString() 并自定义输出的样式。 

     构建自定义异常处理类:

[php] view plain copy
 
 print?
  1. <?php  
  2.   
  3. /** 
  4.  *  
  5.  * 自定义一个异常处理类 
  6.  */  
  7.   
  8. class MyException extends Exception  
  9. {  
  10.     // 重定义构造器使 message 变为必须被指定的属性  
  11.     public function __construct($message, $code = 0) {  
  12.         // 自定义的代码  
  13.   
  14.         // 确保所有变量都被正确赋值  
  15.         parent::__construct($message, $code);  
  16.     }  
  17.   
  18.     // 自定义字符串输出的样式 */  
  19.     public function __toString() {  
  20.         return __CLASS__ . ": [{$this->code}]: {$this->message}\n";  
  21.     }  
  22.   
  23.     public function customFunction() {  
  24.         echo "A Custom function for this type of exception\n";  
  25.     }  
  26. }  
  27. // 例子 1:抛出自定义异常,但没有默认的异常  
  28. echo ‘ 例子 1‘, ‘<br>‘;  
  29. try {  
  30.     // 抛出自定义异常  
  31.     throw new MyException(‘1 is an invalid parameter‘, 5);  
  32. } catch (MyException $e) {      // 捕获异常  
  33.     echo "Caught my exception\n", $e;  
  34.     $e->customFunction();  
  35. } catch (Exception $e) {        // 被忽略  
  36.     echo "Caught Default Exception\n", $e;  
  37. }  
  38. // 执行后续代码  
  39. // 例子 2: 抛出默认的异常  但没有自定义异常  
  40. echo ‘<br>‘, ‘ 例子 2:‘, ‘<br>‘;  
  41. try {  
  42.      // 抛出默认的异常    
  43.     throw new Exception(‘2 isnt allowed as a parameter‘, 6);  
  44. } catch (MyException $e) {      // 不能匹配异常的种类,被忽略  
  45.     echo "Caught my exception\n", $e;  
  46.     $e->customFunction();  
  47. } catch (Exception $e) {// 捕获异常  
  48.     echo "Caught Default Exception\n", $e;  
  49. }  
  50. // 执行后续代码  
  51. // 例子 3: 抛出自定义异常 ,使用默认异常类对象来捕获  
  52. echo ‘<br>‘, ‘ 例子 3:‘, ‘<br>‘;  
  53. try {  
  54.      // 抛出自定义异常   
  55.     throw new MyException(‘3 isnt allowed as a parameter‘, 6);  
  56. } catch (Exception $e) {        // 捕获异常  
  57.     echo "Default Exception caught\n", $e;  
  58. }  
  59.   
  60. // 执行后续代码  
  61. // 例子 4  
  62. echo ‘<br>‘, ‘ 例子 4:‘, ‘<br>‘;  
  63. try {  
  64.     echo ‘No Exception ‘;  
  65. } catch (Exception $e) {        // 没有异常,被忽略  
  66.     echo "Default Exception caught\n", $e;  
  67. }  
  68.   
  69. // 执行后续代码  

           MyException 类是作为旧的 exception 类的一个扩展来创建的。这样它就继承了旧类的所有属性和方法,我们可以使用 exception 类的方法,比如 getLine() 、 getFile() 以及 getMessage()。

4. 嵌套异常处理

    如果在内层 "try" 代码块中异常没有被捕获,则它将在外层级上查找 catch 代码块去捕获。

[php] view plain copy
 
 print?
  1. try {  
  2.     try {  
  3.     throw new MyException(‘foo!‘);  
  4.     } catch (MyException $e) {  
  5.         /* 重新抛出 rethrow it */  
  6.          $e->customFunction();  
  7.         throw $e;  
  8.         
  9.      }  
  10. } catch (Exception $e) {  
  11.         var_dump($e->getMessage());  
  12. }  

 

 

5. 设置顶层异常处理器 (Top Level Exception Handler)

 

    set_exception_handler() 函数可设置处理所有未捕获异常的用户定义函数。  

 

[php] view plain copy
 
 print?
  1. <?php  
  2. function myException($exception)  
  3. {  
  4. echo "<b>Exception:</b> " , $exception->getMessage();  
  5. }  
  6.   
  7. set_exception_handler(‘myException‘);  
  8. throw new Exception(‘Uncaught Exception occurred‘);  

     输出结果:

 

 

[php] view plain copy
 
 print?
  1. Exception: Uncaught Exception occurred  

 

 

6. 异常的规则

  • 需要进行异常处理的代码应该放入 try 代码块内,以便捕获潜在的异常。
  • 每个 try 或 throw 代码块必须至少拥有一个对应的 catch 代码块。
  • 使用多个 catch 代码块可以捕获不同种类的异常。
  • 可以在 try 代码块内的 catch 代码块中再次抛出(re-thrown)异常。

简而言之:如果抛出了异常,就必须捕获它,否则程序终止执行。

 

 

 

在我们实际开发中,错误及异常捕捉仅仅靠try{}catch()是远远不够的。

set_error_handler
一般用于捕捉  E_NOTICE 、E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE
不能捕捉:
E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR and E_COMPILE_WARNING。
一般与trigger_error("...", E_USER_ERROR),配合使用。

 

 

PHP错误处理

在实际开发中,错误及异常捕捉仅仅靠try{}catch()是远远不够的。
所以引用以下几中函数。
a)   set_error_handler
一般用于捕捉  E_NOTICE 、E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE
不能捕捉:
E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR and E_COMPILE_WARNING。
一般与trigger_error("...", E_USER_ERROR),配合使用。

[php] view plain copy
 
 print?
  1. <?php  
  2. // we will do our own error handling  
  3. error_reporting(0);  
  4. function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars)  
  5. {  
  6.     // timestamp for the error entry      
  7.     $dt = date("Y-m-d H:i:s (T)");      
  8.     // define an assoc array of error string      
  9.     // in reality the only entries we should      
  10.     // consider are E_WARNING, E_NOTICE, E_USER_ERROR,      
  11.     // E_USER_WARNING and E_USER_NOTICE      
  12.     $errortype = array (                  
  13.         E_ERROR              => ‘Error‘,                  
  14.         E_WARNING            => ‘Warning‘,                  
  15.         E_PARSE              => ‘Parsing Error‘,                  
  16.         E_NOTICE             => ‘Notice‘,                  
  17.         E_CORE_ERROR         => ‘Core Error‘,                  
  18.         E_CORE_WARNING       => ‘Core Warning‘,                  
  19.         E_COMPILE_ERROR      => ‘Compile Error‘,                  
  20.         E_COMPILE_WARNING    => ‘Compile Warning‘,                  
  21.         E_USER_ERROR         => ‘User Error‘,                  
  22.         E_USER_WARNING       => ‘User Warning‘,                  
  23.         E_USER_NOTICE        => ‘User Notice‘,                  
  24.         E_STRICT             => ‘Runtime Notice‘,                  
  25.         E_RECOVERABLE_ERROR  => ‘Catchable Fatal Error‘                  
  26.     );      
  27.     // set of errors for which a var trace will be saved      
  28.     $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);          
  29.     $err = "<errorentry>\n";      
  30.     $err .= "\t<datetime>" . $dt . "</datetime>\n";      
  31.     $err .= "\t<errornum>" . $errno . "</errornum>\n";      
  32.     $err .= "\t<errortype>" . $errortype[$errno] . "</errortype>\n";      
  33.     $err .= "\t<errormsg>" . $errmsg . "</errormsg>\n";      
  34.     $err .= "\t<scriptname>" . $filename . "</scriptname>\n";      
  35.     $err .= "\t<scriptlinenum>" . $linenum . "</scriptlinenum>\n";      
  36.     if (in_array($errno, $user_errors)) {          
  37.         $err .= "\t<vartrace>" . wddx_serialize_value($vars, "Variables") . "</vartrace>\n";      
  38.     }      
  39.     $err .= "</errorentry>\n\n";  
  40.     echo $err;  
  41. }  
  42. function distance($vect1, $vect2) {      
  43.     if (!is_array($vect1) || !is_array($vect2)) {          
  44.         trigger_error("Incorrect parameters, arrays expected", E_USER_ERROR);          
  45.         return NULL;      
  46.     }      
  47.     if (count($vect1) != count($vect2)) {          
  48.         trigger_error("Vectors need to be of the same size", E_USER_ERROR);          
  49.         return NULL;      
  50.     }   
  51.     for ($i=0; $i<count($vect1); $i++) {          
  52.         $c1 = $vect1[$i]; $c2 = $vect2[$i];          
  53.         $d = 0.0;          
  54.         if (!is_numeric($c1)) {              
  55.         trigger_error("Coordinate $i in vector 1 is not a number, using zero",E_USER_WARNING);              
  56.         $c1 = 0.0;          
  57.     }          
  58.     if (!is_numeric($c2)) {              
  59.         trigger_error("Coordinate $i in vector 2 is not a number, using zero",E_USER_WARNING);              
  60.         $c2 = 0.0;          
  61.     }  
  62.     $d += $c2*$c2 - $c1*$c1;      
  63.     }      
  64.     return sqrt($d);  
  65. }  
  66.   
  67. $old_error_handle = set_error_handler("userErrorHandler");  
  68. $t = I_AM_NOT_DEFINED;  //generates a warning  
  69.   
  70. // define some "vectors"  
  71. $a = array(2, 3, "foo");  
  72. $b = array(5.5, 4.3, -1.6);  
  73. $c = array(1, -3);  
  74.   
  75. //generate a user error  
  76. $t1 = distance($c,$b);  
  77.   
  78. // generate another user error  
  79. $t2 = distance($b, "i am not an array") . "\n";  
  80.   
  81. // generate a warning  
  82. $t3 = distance($a, $b) . "\n";  
  83. ?>  

b)   set_exception_handler 
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。 
与throw new Exception(‘Uncaught Exception occurred‘),连用。
[php] view plain copy
 
 print?
  1. <?php  
  2. // we will do our own error handling  
  3. error_reporting(0);  
  4. function exceptHandle($errno, $errmsg, $filename, $linenum, $vars)  
  5. {  
  6.     // timestamp for the error entry      
  7.     $dt = date("Y-m-d H:i:s (T)");      
  8.     // define an assoc array of error string      
  9.     // in reality the only entries we should      
  10.     // consider are E_WARNING, E_NOTICE, E_USER_ERROR,      
  11.     // E_USER_WARNING and E_USER_NOTICE      
  12.     $errortype = array (                  
  13.         E_ERROR              => ‘Error‘,                  
  14.         E_WARNING            => ‘Warning‘,                  
  15.         E_PARSE              => ‘Parsing Error‘,                  
  16.         E_NOTICE             => ‘Notice‘,                  
  17.         E_CORE_ERROR         => ‘Core Error‘,                  
  18.         E_CORE_WARNING       => ‘Core Warning‘,                  
  19.         E_COMPILE_ERROR      => ‘Compile Error‘,                  
  20.         E_COMPILE_WARNING    => ‘Compile Warning‘,                  
  21.         E_USER_ERROR         => ‘User Error‘,                  
  22.         E_USER_WARNING       => ‘User Warning‘,                  
  23.         E_USER_NOTICE        => ‘User Notice‘,                  
  24.         E_STRICT             => ‘Runtime Notice‘,                  
  25.         E_RECOVERABLE_ERROR  => ‘Catchable Fatal Error‘                  
  26.     );      
  27.     // set of errors for which a var trace will be saved      
  28.     $err = "<errorentry>\n";      
  29.     $err .= "\t<datetime>" . $dt . "</datetime>\n";      
  30.     $err .= "\t<errornum>" . $errno . "</errornum>\n";      
  31.     $err .= "\t<errortype>" . $errortype[$errno] . "</errortype>\n";      
  32.     $err .= "\t<errormsg>" . $errmsg . "</errormsg>\n";      
  33.     $err .= "\t<scriptname>" . $filename . "</scriptname>\n";      
  34.     $err .= "\t<scriptlinenum>" . $linenum . "</scriptlinenum>\n";      
  35.     if (1) {          
  36.         $err .= "\t<vartrace>" . wddx_serialize_value($vars, "Variables") . "</vartrace>\n";      
  37.     }      
  38.     $err .= "</errorentry>\n\n";  
  39.     echo $err;  
  40. }  
  41. $old_except_handle = set_exception_handler("exceptHandle");  
  42. //$t = I_AM_NOT_DEFINED;    //generates a warning  
  43. $a;  
  44. throw new Exception(‘Uncaught Exception occurred‘);      
  45. ?>  

c)   register_shutdown_function 
执行机制是:php把要调用的函数调入内存。当页面所有PHP语句都执行完成时,再调用此函数。
一般与trigger_error("...", E_USER_ERROR),配合使用。
[php] view plain copy
 
 print?
  1. <?php  
  2. error_reporting(0);  
  3. date_default_timezone_set(‘Asia/Shanghai‘);  
  4. register_shutdown_function(‘my_exception_handler‘);  
  5.   
  6. $t = I_AM_NOT_DEFINED;  //generates a warning  
  7. trigger_error("Vectors need to be of the same size", E_USER_ERROR);       
  8.   
  9. function my_exception_handler()  
  10. {  
  11.     if($e = error_get_last()) {  
  12.     //$e[‘type‘]对应php_error常量  
  13.     $message = ‘‘;  
  14.     $message .= "出错信息:\t".$e[‘message‘]."\n\n";  
  15.     $message .= "出错文件:\t".$e[‘file‘]."\n\n";  
  16.     $message .= "出错行数:\t".$e[‘line‘]."\n\n";  
  17.     $message .= "\t\t请工程师检查出现程序".$e[‘file‘]."出现错误的原因\n";  
  18.     $message .= "\t\t希望能您早点解决故障出现的原因<br/>";  
  19.     echo $message;  
  20.     //sendemail to  
  21.     }  
  22. }  
  23. ?>  


2、简要说明错误处理:

 

1)、使用指定的文件记录错误报告日志

      使用指定的文件记录错误报告日志使用指定的文件记录错误报告日志使用指定的文件记录错误报告日志 如果使用自己指定的文件记录错误日志,一定要确保将这个文件存放在文档根目录之外,以减少遭到攻击的可能。并且该文件一定要让PHP脚本的执行用户(Web服务器进程所有者)具有写权限。假设在Linux操作系统中,将/usr/local/目录下的error.log文件作为错误日志文件,并设置Web服务器进程用户具有写的权限。然后在PHP的配置文件中,将error_log指令的值设置为这个错误日志文件的绝对路径。

 

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

 

[plain] view plain copy
 
 print?
  1. error_reporting  =  E_ALL                   ;将会向PHP报告发生的每个错误     
  2. display_errors = Off                        ;不显示满足上条 指令所定义规则的所有错误报告     
  3. log_errors = On                             ;决定日志语句记录的位置     
  4. log_errors_max_len = 1024                   ;设置每个日志项的最大长度     
  5. 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数据库出现问题的处理为例,该函数的使用如下所示: