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

     
  • 相关阅读:
    MySQL主从复制报错 Errno 1205
    MySQL添加索引优化SQL
    MySQL通过添加索引解决线上数据库服务器压力大问题
    手把手教你搭建MySQL双主MM+keepalived高可用架构
    SQLSERVER 维护计划无法删除
    Form表单中Post与Get方法的区别
    ASP.NET MVC中常用的ActionResult类型
    Web安全相关(五):SQL注入(SQL Injection)
    Web安全相关(四):过多发布(Over Posting)
    Web安全相关(三):开放重定向(Open Redirection)
  • 原文地址:https://www.cnblogs.com/yang01/p/7326836.html
Copyright © 2020-2023  润新知