2017.8.10

一、PHP的错误和异常处理

1、异常与错误的概述
  PHP中什么是异常:
  程序在运行中出现不符合预期的情况,允许发生(你也不想让他出现不正常的情况)但他是一种不正常的情况,按照我们的正常逻辑本不该出的错误,但仍然会出现的错误,属于逻辑和业务流程的错误,而不是编译或者语法上的错误。
  PHP中什么是错误:
  属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice都是错误,只是他们的级别不同而已,并且错误是不能被try-catch捕获的。

2、ERROR的级别
  只有熟悉错误级别才能对错误捕捉有更好的认识。 ERROR有不同的错误级别:

 Fatal Error:致命错误(脚本终止运行)
          E_ERROR         // 致命的运行错误,错误无法恢复,暂停执行脚本
          E_CORE_ERROR    // PHP启动时初始化过程中的致命错误
          E_COMPILE_ERROR // 编译时致命性错,就像由Zend脚本引擎生成了一个E_ERROR
          E_USER_ERROR    // 自定义错误消息。像用PHP函数trigger_error(错误类型设置为:E_USER_ERROR)
  
     Parse Error:编译时解析错误,语法错误(脚本终止运行)
          E_PARSE  //编译时的语法解析错误
  
     Warning Error:警告错误(仅给出提示信息,脚本不终止运行)
         E_WARNING         // 运行时警告 (非致命错误)。
         E_CORE_WARNING    // PHP初始化启动过程中发生的警告 (非致命错误) 。
         E_COMPILE_WARNING // 编译警告
         E_USER_WARNING    // 用户产生的警告信息

     Notice Error:通知错误(仅给出通知信息,脚本不终止运行)
         E_NOTICE      // 运行时通知。表示脚本遇到可能会表现为错误的情况.
         E_USER_NOTICE // 用户产生的通知信息。

  由此可知有5类是产生ERROR级别的错误,这种错误直接导致PHP程序退出可以定义成: ERROR = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_PARSE

如果希望在PHP脚本中,遇到以上某个错误时,报告错误消息,必须在配置文件php.ini中将display_errors指令的值设置为On,来开启PHP输出错误报告的功能。

在PHP脚本中调用ini_set()函数,强制在该脚本中启动display_errors指令。并通过error_reporting()函数设置错误级别为E_ALL,报告所有错误警告和注意。

3、自定义错误处理

1:set_error_handler()
  看到这个名字估计就知道什么意思了,这个函数用于捕获错误,设置一个用户自定义的错误处理函数。

 <?php
     set_error_handler('zyferror');//使用此句把错误的处理交给用户自定义的函数zyferror();处理,error_reporting()函数将会失效。
     function zyferror($type, $message, $file, $line)
     {
       var_dump('<b>set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .</b><br />');
     }
 ?>

  当程序出现错误的时候自动调用此方法,不过需要注意一下两点:第一,如果存在该方法,相应的error_reporting()就不能在使用了。所有的错误都会交给自定义的函数处理。第二,此方法不能处理以下级别的错误:E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,set_error_handler() 函数所在文件中产生的E_STRICT,该函数只能捕获系统产生的一些Warning、Notice级别的错误

  并且他有多种调用的方法

<?php
      // 直接传函数名 NonClassFunction
      set_error_handler('function_name'); 
 
      // 传 class_name && function_name
      set_error_handler(array('class_name', 'function_name')); 
 ?>

2:register_shutdown_function()
  捕获PHP的错误:Fatal Error、Parse Error等,这个方法是PHP脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有错误error_get_last();返回的信息
  [type]           - 错误类型
  [message] - 错误消息
  [file]              - 发生错误所在的文件
  [line]             - 发生错误所在的行

<?php
    register_shutdown_function('zyfshutdownfunc');//在退出脚本前,调用并执行在该函数中注册的函数。
     function zyfshutdownfunc()
     {
         if ($error = error_get_last()) //通过error_get_last()函数获取错误信息,赋值给$error数组,通过下面语句打印输出错误信息
{
var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>'); } }
      var_dump(23+-+); //此处语法错误
echo "test";//函数会在输出此句后,打印出上面一句的错误
?>

 通过这种方法就可以巧妙的打印出程序结束前所有的错误信息

 3:set_exception_handler()
  设置默认的异常处理程序,用在没有用try/catch块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会中止。看一下用法:

1 <?php
2     set_exception_handler('zyfexception');//自定义异常处理函数,脚本程序会在异常处理程序执行结束后停止执行
3     function zyfexception($exception)
4     {
5         var_dump("<b>set_exception_handler: Exception: " . $exception->getMessage()  . '</b>');
6     }
7     throw new Exception("zyf exception");
8 ?>

以上内容来自: http://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html。

4、错误日志

对于PHP开发者来说,一旦某个产品投入使用,应该立即将display_errors选项关闭,以免因为这些错误所透露的路径、数据库连接、数据表等信息而遭到黑客攻击。但是,任何一个产品在投入使用后,都难免会有错误出现,那么如何记录一些对开发者有用的错误报告呢?我们可以在单独的文本文件中将错误报告作为日志记录。错误日志的记录,可以帮助开发人员或者管理人员查看系统是否存在问题。

如果需要将程序中的错误报告写入错误日志中,只要在PHP的配置文件中,将配置指令log_errors开启即可。错误报告默认就会记录到Web服务器的日志文件里,例如记录到Apache服务器的错误日志文件error.log中。当然也可以记录错误日志到指定的文件中或发送给系统syslog,分别介绍如下:

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()函数,送出一个用户自定义的错误信息。该函数的原型如下所示:

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中。

5、 异常处理

异常(Exception)处理用于指定的错误发生时改变脚本的正常流程,是PHP 5中的一个新的重要特性。异常处理是一种可扩展、易维护的错误处理统一机制,并提供了一种新的面向对象的错误处理方式。

1.异常处理实现

异常处理和编写程序的流程控制相似,所以也可以通过异常处理实现一种另类的条件选择结构。异常就是在程序运行过程中出现的一些意料之外的事件,如果不对此事件进行处理,则程序在执行时遇到异常将崩溃。处理异常需要在PHP脚本中使用以下语句:

try {                     //所有需要进行异常处理的代码都必须放入这个代码块内

   … …                  //在这里可以使用throw语句抛出一个异常对象

}catch(ex1) {               //使用该代码块捕获一个异常,并进行处理

   … …                 //处理发生的异常,也可再次抛出异常

}

在PHP代码中所产生的异常可以被throw语句抛出并被catch语句捕获需要进行异常处理的代码都必须放入try代码块内,以便捕获可能存在的异常。每一个try至少要有一个与之对应的catch,也不能出现单独的catch,另外try和cache之间也不能有任何的代码出现。一个异常处理的简单实例如下所示:

<?php

         try {
                   $error = 'Always throw this error';

                   throw new Exception($error);      //创建一个异常对象,通过throw语句抛出

                   echo 'Never executed';            //从这里开始,try代码块内的代码将不会再被执行

         } catch (Exception $e) {

                   echo 'Caught exception: ',  $e->getMessage(), "
";  //输出捕获的异常消息
         }

         echo 'Hello World';                    //程序没有崩溃继续向下执行

?>

在上面的代码中,如果try代码块中出现某些错误,我们就可以执行一个抛出异常的操作。在某些编程语言中,例如Java,在出现异常时将自动抛出异常。而在PHP中,异常必须手动抛出。throw关键字将触发异常处理机制,它是一个语言结构,而不是一个函数,但必须给它传递一个对象作为值。在最简单的情况下,可以实例化一个内置的Exception类,就像以上代码所示那样。如果在try语句中有异常对象被抛出,该代码块不会再继续向下执行,而直接跳转到catch中执行。并传递给catch代码块一个对象,也可以理解为被catch代码块捕获的对象,其实就是导致异常被throw语句抛出的对象。在catch代码块中可以简单的输出一些异常的原因,也可以是try代码块中任务的另一个版本解决方案,此外,也可以在这个catch代码块中产生新的异常。最重要的是,在异常处理之后,程序不会崩溃,而会继续执行。

6、捕获多个异常

在try代码块之后,必须至少给出一个catch代码块,也可以将多个catch代码块与一个try代码块进行关联。如果每个catch代码块可以捕获一个不同类型的异常,那么使用多个catch就可以捕获不同的类所产生的异常。当产生一个异常时,PHP将查询一个匹配的catch代码块。如果有多个catch代码块,传递给每一个catch代码块的对象必须具有不同的类型,这样PHP可以找到需要进入哪一个catch代码块。当try代码块不再抛出异常或者找不到catch能匹配所抛出的异常时,PHP代码就会在跳转到最后一个 catch 的后面继续执行。多个异常的捕获的示例如下:

内置的异常处理类(Exception)

<?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 "

以上部分内容来自:http://www.cnblogs.com/laojie4321/p/4187620.html。

 
原文地址:https://www.cnblogs.com/yang01/p/7326836.html