PHP的错误和异常处理

错误处理


错误报告级别

        如果希望在php脚本中,将某个级别的错误消息报告给他,则必须在配置文件php.ini中,将display_errors指令的值设置为On,开启输出错误报告的功能。也可以在php脚本中调用ini_set()函数,动态配置文件php.ini中的某个指令。

        如果display_error被启动,就会显示满足已设置的错误级别的所有错误报告。当用户在访问网站时,看到现实的这些消息不仅会感到迷惑,而且还可能会更过多地泄露有关服务器的信息,是服务器变得不安全。
调整错误报告级别
        调整错误报告的级别实现,可以通过以下两种方法设置错误报告级别:
  • 可以通过在配置文件php.ini中,修改配置指令error_reporting的值,修改成功后重新启动Web服务器,则每个PHP脚本都可以按调整后的错误报告。下面是修改php.ini配置文件的示例。列出几种为error_reporting指令设置不同级别的值的方式,可以把位运算符[&(与)、|(或)、~(非)]和错误级别常量一起使用。
1
2
3
4
5
6
;可以抛出任何非注意的错误,默认值
error_reporting = E_ALL &~ E_NOTICE
;只考虑致命的运行时错误、解析错误和核心错误
error_reporting = E_ERROE | E_PARSE | E_CORE_ERROR
;报告除用户导致的错误之外的所有错误
error_reporting = E_ALL &~(E_USER_ERROR | E_USER_WARNING |E_USER_NOTICE)
  • 或者在php脚本中使用error_reporting()函数,基于各个脚本来调整这种行为。这个函数用于确定php应该在特定的页面内报告哪些类型的错误。该函数获取一个数字或错误级别常量作为参数。
1
2
3
error_reporting(0);                //设置为0会完全关闭错误报告
error_reporting(E_ALL);            //将会向PHP报告发生的每个错误
error_reporting(E_ALL & ~ E_NOTICE); //可以抛出任何非注意的错误报告
其他配置指令
使用trigger_error()函数来替代die()
        die()等同于exit(),两者如果执行都会终止PHP程序,而且可以在退出程序之前输出一些错误报告。trigger_error()则可以生成一个用户警告来代替,是程序更具有灵活性。
自定义错误处理
        通常使用set_error_handler()函数去设置用户自定义的错误处理函数,该函数用于创建运行时期间的用户自己的错误处理方法,返回就的错误处理程序,若失败,则返回null。该函数有两个参数,其中第一个参数是必选的,需要一个回调函数,规定发生错误时运行的函数。这个回调函数一定要声明4个参数,否则无效,按顺序分别是是否存在错误、错误信息、错误文件和错误行号。set_error_handler()函数的第二个参数是可选的,规定在哪个错误报告级别会显示用户定义的错误。默认是"E_ALL"。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php
    error_reporting(0);                 //屏蔽程序中的错误
     
    /**
        定义Error_Handler函数,作为set_error_handler()函数的第一个参数"回调"
        @param  int     $error_level        错误级别
        @param  string  $error_message  错误信息
        @param  string  $file           错误所在文件
        @param  int     $lin                错误所在行数
    */
    function error_handler($error_level, $error_message, $file, $line) {
        $EXIT = FALSE;
        switch( $error_level ) {
            //提醒级别
            case E_NOTICE:
            case E_USER_NOTICE;
                $error_type = 'Notice';
                break;
                 
            //警告级别
            case E_WARNING:
            case E_USER_WARNING:
                $error_type = 'Warning';
                break;
             
            //错误级别
            case E_ERROR:
            case E_USER_ERROR:
                $error_type = 'Fatal Error';
                $EXIT = TRUE;
                break;
                 
            //其他末知错误
            default:
                $error_type = 'Unknown';
                $EXIT = TRUE;
                break;
        }
         
        //直接打印错误信息, 也可以写文件,写数据库,反正错误信息都在这,任你发落
        printf ("<font color='#FF0000'><b>%s</b></font>: %s in <b>%s</b> on line <b>%d</b><br>\n", $error_type, $error_message, $file, $line);
         
        //如果错误影响到程序的正常执行,跳转到友好的错误提示页面
        if(TRUE == $EXIT) {
            echo '<script>location = "err.html"; </script>';
        }
    }
     
    //这个才是关键点, 把错误的处理交给error_handler()
    set_error_handler('error_handler');
     
    //使用末定义的变量要报 notice 的
    echo $novar;
     
    //除以0要报警告的
    echo 3/0;
     
    //自定义一个错误
    trigger_error('Trigger a fatal error', E_USER_ERROR);
  • 系统直接报Fital Error这个方法是捕获不到的,遇到这种错误是必须解决的,所以系统会直接终止程序运行。
  • E_ERROR、E_PARSE、E_CORE_ERROR、ERROR_WARNING/E_COMPILE_ERROR、ERROR_COMPILE_WARNING是不会被这个句柄处理的,也就是会用原始的方式显示出来。不过出现这些错误都是编译或PHP内核错误,在通常情况下不会发生。
  • 使用set_error_handler()后,error_reporting()将会失效。也就是所有的错误都会交给自定义的函数处理。
写错误日志
 1、使用指定的文件记录错误报告日志

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

1
2
3
4
5
6
7
8
9
10
;将会向php报告发生的每个错误
error_reporting = E_ALL
;不显示满足上条指令所定义规则的所有错误报告
display_errors = Off
;决定日志语句记录的位置
log_errors = On
;设置每个日志想的最大长度
log_errors_max_len = 1024
;指定产生的错误报告写入的日志文件位置
error_log = /usr/local/error.log

        还可以使用php中的error_log()函数,送出一个用户自定义的错误信息。

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

        第一个参数:必选项,即为要送出的错误信息。

        第二个参数:为整数值,0表示送到操作系统的日志中;1则使用PHP的Mail()函数,发送信息到某Email地址处,第四个参数也会用到;2则将错误信息送到TCP服务器中,此时第三个参数destination表示目的地ip及Port;3则将信息存到文件destination中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php  
    if(!Ora_Logon($username, $password)){  
        //将错误消息写入到操作系统日志中
        error_log("Oracle数据库不可用!", 0);         
    }
     
    if(!($foo=allocate_new_foo()){
        //发送到管理员邮箱中
        error_log("出现大麻烦了!", 1, "webmaster@www.mydomain.com");  
    }
     
    //发送到本机对应5000端口的服务器中
    error_log("搞砸了!",   2,   "localhost:5000");    
    //发送到指定的文件中
    error_log("搞砸了!",   3,   "/usr/local/errors.log"); 

2、错误信息记录到操作系统的日志里

        

1
2
3
4
5
6
7
8
9
10
;将会向php报告发生的每个错误
error_reporting = E_ALL
;不显示满足上条指令所定义规则的所有错误报告
display_errors = Off
;决定日志语句记录的位置
log_erros = On
;设置每个日志项的最大长度
log_errors_max_len = 1024
;指定产生的错误报告写入操作系统的日志里
error_log = syslog

php还允许想系统syslog中发送定制的消息,php为这个特性提供了需要一起使用的4个专用函数。

  • define_syslog_variables()

        使用openlog()、syslog()及closelog()三个函数之前必须先调用该函数,因为在调用该函数时,他会根据现在的系统环境为下面三个函数初使用化一些必需的常量。

  • openlog()

        打开一个和当前系统中日志器的连接,为向系统插入日志消息做好准备。并将提供的第一个字符串参数插入到每个日志消息中,该函数还需要指定两个将在日志上下文使用的参数。

  • syslog()

       该函数向系统日志中发送一个定制消息。需要两个必选参数,第个参数通过指定一个常量定制消息的优先级。第二个参数则是像系统日志中发送定制的消息,需要提供一个消息字符串,也可以是php引擎在运行时提供的错误字符串。

  • closelog()
        该函数在向系统日志中发送完成定制消息以后调用,关闭由openlog()函数打开的日志连接。

1
2
3
4
5
6
7
<?php
    define_syslog_variables();
     
    openlog("PHP5", LOG_PID , LOG_USER);
    syslog(LOG_WARNING, "警告报告向syslog中发送的演示,警告时间:".date("Y/m/d H:i:s"));
     
    closelog();

异常处理

异常处理实现

        在php中,异常必须手动抛出。throw关键字将触发异常处理机制,他是一个语言结构,而不是一个函数,但必须给它传递一个对象作为值。如果在try语句中游艺场对象被抛出,改代码块不会再继续向下执行,而直接转到catch中执行。并传给catch代码块一个对象,也可以理解为被catch代码块不活的对象,其实就是导致异常常被throw语句抛出的对象。

扩展php那只的异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
/* 自定义的一个异常处理类 ,但必须是扩展内异常处理类的子类  */
class MyException extends Exception{
    //重定义构造器是第一个参数message变为必须被指定的属性
    public function __construct($message, $code=0){
        //可以在这里定义一些自己的代码
        //建议同时调用parent::construct()来检查所有的变量是否已被赋值
        parent::__construct($message,$code);
    }
     
    //重写父类方法,自定义字符串输出的样式
    public function __toString(){
        return __CLASS__.":[".$this->code."]".$this->message."<br/>";
    }
     
    //为这个异常自定义一个处理方法
    public function customFunction(){
        echo "按自定义的方法处理出现的这个类型的异常<br/>";
    }
     
}
 
try {
    $error = "允许抛出这个错误";
    throw new MyException($error);
    echo 'Never executed';
} catch (MyException $e) {
    echo '捕获异常:'.$e;
    $e->customFunction();
}
echo "你好!";

捕获多个异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
    /* 自定义的一个异常处理类,但必须是扩展内异常处理类的子类 */
    class MyException extends Exception{
        //重定义构造器使第一个参数 message 变为必须被指定的属性
        public function __construct($message, $code=0){
            //可以在这里定义一些自己的代码
            //建议同时调用 parent::construct()来检查所有的变量是否已被赋值
            parent::__construct($message, $code);
        }
        //重写父类中继承过来的方法,自定义字符串输出的样式
        public function __toString() {
            return __CLASS__.":[".$this->code."]:".$this->message."<br>";
        }
 
        //为这个异常自定义一个处理方法
        public function customFunction() {
            echo "按自定义的方法处理出现的这个类型的异常";
        }
    }
     
    /* 创建一个用于测试自定义扩展的异常类MyException */
    class TestException {
        public $var;                                        //用来判断对象是否创建成功的成员属性
 
        function __construct($value=0) {                    //通过构造方法的传值决定抛出的异常
            switch($value){                                 //对传入的值进行选择性的判断
                case 1:                                     //传入参数1,则抛出自定义的异常对象
                    throw new MyException("传入的值“1” 是一个无效的参数", 5); break;
                case 2:                                     //传入参数2,则抛出PHP内置的异常对象
                    throw new Exception("传入的值“2”不允许作为一个参数", 6); break;
                default:                                    //传入参数合法,则不抛出异常
                    $this->var=$value;   break;              //为对象中的成员属性赋值
                     
            }
        }
    }
     
    /* 示例1,在没有异常时,程序正常执行,try中的代码全部执行并不会执行任何catch区块 */
    try{
        $testObj = new TestException();                     //使用默认参数创建异常的测试类对象
        echo "***********<br>";                               //没有抛出异常这条语句就会正常执行
    }catch(MyException $e){                                 //捕获用户自定义的异常区块
        echo "捕获自定义的异常:$e <br>";                  //按自定义的方式输出异常消息
        $e->customFunction();                                //可以调用自定义的异常处理方法
    }catch(Exception $e) {                                  //捕获PHP内置的异常处理类的对象
        echo "捕获默认的异常:".$e->getMessage()."<br>";    //输出异常消息
    }  
    var_dump($testObj);          //判断对象是否创建成功,如果没有任何异常,则创建成功
 
    /* 示例2,抛出自定义的异常,并通过自定义的异常处理类捕获这个异常并处理 */
    try{   
        $testObj1 = new TestException(1);                    //传1时,抛出自定义异常
        echo "***********<br>";                                //这个语句不会被执行
    }catch(MyException $e){                                  //这个catch区块中的代码将被执行
        echo "捕获自定义的异常:$e <br>";
        $e->customFunction();
    }catch(Exception $e) {                                  //这个catch区块不会执行
        echo "捕获默认的异常:".$e->getMessage()."<br>";
    }  
    var_dump($testObj1);                                    //有异常产生,这个对象没有创建成功
 
    /* 示例2,抛出内置的异常,并通过自定义的异常处理类捕获这个异常并处理 */
    try{
        $testObj2 = new TestException(2);                   //传入2时,抛出内置异常
        echo "***********<br>";                           //这个语句不会被执行
    }catch(MyException $e){                                 //这个catch区块不会执行
        echo "捕获自定义的异常:$e <br>";
        $e->customFunction();
    }catch(Exception $e) {                                  //这个catch区块中的代码将被执行
        echo "捕获默认的异常:".$e->getMessage()."<br>";
    }  
    var_dump($testObj2);                                    //有异常产生,这个对象没有创建成功


错误查询

error_get_last():

        函数获取最后发生的错误。 

        该函数以数组的形式返回最后发生的错误。 

        返回的数组包含 4 个键和值: 

  • type:错误类型。
  • message:错误消息。
  • file:发生错误所在的文件。
  • line:发生错误所在的行。

        


        







posted @ 2016-01-19 16:02  staven  阅读(685)  评论(0编辑  收藏  举报