php的错误与异常处理-转载

请注意,没有特殊说明,例子的php Version<7

说起php异常,大家首先会想到try-catch,那好,我们下面来看一个例子:

<?php
    $num = 0;
    try{
        echo 1/$num;
    }catch(Exception $e){
        echo $e->getMessage();
    }

这里有一个问题问大家:这段程序能够正确的捕获到除0的错误吗? 如果回答能,那么你就把这篇文章看完吧!应该能够学到一点东西。

本文章分为五个部分介绍我的异常处理的理解:

一、异常与错误的概述

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

上边的说法是有前提条件的:php 中,因为在其他语言中就不能这样下结论了,也就是说异常和错误的说法在不同的语言有不同的说法。在 php 中任何自身的错误或者是非正常的代码都会当作错误对待,并不会以异常的形式抛出,但是也有一些情况会当作异常和错误同时抛出(据说是,我们有找到合适的例子)。也就是说,你想在数据库连接失败的时候自动捕获异常是行不通的,因为这就不是异常,是错误。但是在java中就不一样了,他会把很多和预期不一样的行为都当作异常来进行捕获。

php的异常处理和鸡肋?
    在上面的分析中我们可以看出,php并不能主动的抛出异常,但是你可以手动的抛出异常,这就很无语了,如果你知道哪里会出问题,你添加 if else 解决不就行了吗,为啥还要手动抛出,既然能手动抛出就证明这个不是异常,而是意料之中。以我的理解,这就是php异常处理鸡肋的地方(不一定对啊)。所以php的异常机制不是那么完美,但是使用过框架的童鞋都知道有这个情况:你在框架中直接写开头那段php 自动捕获异常的代码是可以的,这是为什么?看过源码的同学都知道框架中都会涉及三个函数: register_shoudown_functionset_error_handlerset_exception_handler 后面我们会着重讲解这三个黑科技,通过这几个函数我们可以实现php假的自动捕获异常和错误。

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_WARING //运行时警告(非致命错误)
    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异常处理中的黑科技

    前面提到的框架中时可以捕获所有的错误和异常的,之所以能实现应该使用了黑科技,哈哈~,其实也不是什么黑科技,主要时三个重要的函数:

  • 1:set_error_handler()
    看这个名字估计就知道什么意思了,这个函数用于捕获错误,设置一个用户自定义的错误处理函数。
<?php
    ser_error_handler('testError');
    function testError($type,$message,$file,$line){
        var_dump($type,$message,$file,$line);
    }

    当程序出现错误的时候自动调用此方法,不过需要注意一下两点:

  • a.如果存在该方法,响应的 error_reporting() 就不能在使用了。所有的错误都会交给自定义的函数处理。

  • b.此方法不能处理以下级别的错误:E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING

set_error_handler() 函数所在文件中产生的 E_STRICT ,该函数只能捕获系统产生的一些 Warning 、Notice级别的错误 。并且他有多种调用方法:

<?php
    //直接传函数名
    set_error_handler('function_name');
    //传类名以及类下面的方法
    set_error_handler('class_name','class_method');
  • 2.register_shutdown_function()
        捕获php的错误:Fatal Error 、 Parse Error等,这个方法是php脚本执行结束前最后一个调用的函数,比如脚本错误, die()、exit() 、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助与一个函数: error_get_last() ;这个函数可以拿到本次执行产生的所有错误。error_get_last() 返回的信息:
    //['type'] - 错误类型
    //['message'] - 错误消息
    //['file'] - 发生错误所在的文件
    //['line'] - 发生错误所在的行 
<?php
    refister_shutdown_function('testShoutdownfunc');
    function testShoutdownfunc(){
        if($error = errir_get_last()){
            var_dump($error['type'],$error['message'],$error['file'],$error['line'])
        }
    }

     通过这种方法就可以巧妙的打印出程序结束前所有的错误信息。但是我在测试的时候发现并不是所有的错误终止后都会调用这个函数,看一下下面这个测试文件。内容是:


<?php
    register_shutdown_function('testShoutdownfunc');
    function zyfshutdownfunc()
    {
        if ($error = error_get_last()) {
            var_dump($error['type'],$error['message'] ,$error['file'],$error['line']);
        }
    }
    var_dump(23+-+); //此处语法错误
?>

     自己可以试一下,你可以看到根本就不会触发zyfshutdownfunc()函数,其实这是一个语法错误,直接报了一个:
Parse error: syntax error, unexpected ')' in /www/mytest/exception/try-catch.php on line 71

    由此引出一个奇葩的问题:问什么不能触发,为什么框架中是可以的?其实原因很简单,只在 parse-time 出错时是不会调用本函数的。只有在 run-time 出错的时候,才会调用本函数,我的理解是语法检查器前没有执行 register_shutdown_function() 去把需要注册的函数放到调用的堆栈中,所以就根本不会运行。那框架中为什么任何错误都能进入到 register_shutdown_function() 中呢,其实在框架中一般会有统一的入口 index.php ,然后每个类库文件都会通过 include 的方式加载到 index.php 中,相当与所有的程序都会在 index.php 中聚集,同样,你写的具有语法错误的文件也会被引入到入口文件中,这样的话,调用框架,执行 index.phpindex.php 本身并没有语法错误,也就不会产生 parse-time 错误,而是 include 文件出错了,是 run-time 的时候出错了,所以框架执行完之后就会触发 register_shutdown_function() ; 所以现在可是试一下这个写法,这样就会触发 testShoutdownfunc() 回调了:

//a.php文件
<?php
    //模拟语法错误
    var_dump(32+-+);
?>
//b.php文件
<?php
    register_shutdown_function('testShotdownfunc');
    function testShotdownfunc(){
        if($error = error_get_last()){
             var_dump($error['type'],$error['message'],$error['file'],$error['line']);
        }
        require 'a.php';
    }
  • 3.set_exception_handler()
        设置默认的异常处理程序,用在没有用 try-catch 块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会终止。看一下用法:
<?php
    set_exception_handler('testException');
    function testException($exception){
            var_dump($exception->getMessage());
    }
    throw new Exception('test');

四.巧妙的捕获错误和异常

  • 1.把错误以异常的形式抛出(不能完全抛出)
      由上面的讲解我们知道,php中的错误是不能以异常的像是捕获的,但是我们需要让他们抛出,已达到扩展 try-catch的影响范围,我们前面讲到过set_error_handler() 方法,他是干嘛用的,他是捕获错误的,所以我们就可以借助他来吧错误捕获,然后再以异常的形式抛出,ok,试试下面的写法:
<?php
    set_error_handler('zyferror');
    function zyferror($type, $message, $file, $line)
    {
        throw new \Exception($message . 'zyf错误当做异常');
    }

    $num = 0;
    try {
        echo 1/$num;

    } catch (Exception $e){
        echo $e->getMessage();
    }
15.?>

好了,试一下,会打印出:Division by zero zyf123
流程:本来是除0错误,然后触发set_error_handler(),在set_error_handler() 中相当与杀了个回马枪,再把错误信息以异常的形式抛出来,这样就可以实现错误以异常的形式抛出。大家要注意:这样做是有缺点的,会受到 set_error_handler() 函数捕获级别的限制。

  • 2.捕获所有的错误
      由 set_error_handler() 可知,他能够捕获一部分错误,不能捕获系统级 E_ERROR、E_PARSE 等错误,但是这部分可以由 register_shutdown_function() 捕获。所以两者结合能出现很好的功能。看下面的程序:
a.php内容:
<?
    // 模拟Fatal error错误
    //test();

    // 模拟用户产生ERROR错误
    //trigger_error('zyf-error', E_USER_ERROR);

    // 模拟语法错误
    var_dump(23+-+);

    // 模拟Notice错误
    //echo $f;

    // 模拟Warning错误
    //echo '123';
    //ob_flush();
    //flush();
    //header("Content-type:text/html;charset=gb2312");
?>
b.php内容:
<?
    error_reporting(0);
    register_shutdown_function('zyfshutdownfunc');
    function zyfshutdownfunc()
    {
        if ($error = error_get_last()) {
            var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>');
        }
    }

    set_error_handler('zyferror');
    function zyferror($type, $message, $file, $line)
    {
        var_dump('<b>set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .</b><br />');
    }

    require 'a.php';
?>

到此就可以解释开头的那个程序了吧,test.php 如果是单文件执行是不能捕获到错误的,如果你在框架中执行就是可以的,当然你按照我上面介绍的来扩展也是可以的。

五、自定义异常处理和异常嵌套

  1:自定义异常处理
  在复杂的系统中,我们往往需要自己捕获我们需要特殊处理的异常,这些异常可能是特殊情况下抛出的。所以我们就自己定义一个异常捕获类,该类必须是 exception 类的一个扩展,该类继承了 PHP 的 exception 类的所有属性,并且我们可以添加自定义的函数,使用的时候其实和之前的一样,大致写法如下:

1.<?php
2.    class zyfException extends Exception
3.    {
4.        public function errorzyfMessage()
5.        {
6.            return 'Error line ' . $this->getLine().' in ' . $this->getFile()
7.                .': <b>' . $this->getMessage() . '</b> Must in (0 - 60)';
8.        }
9.    }
10.
11.    $age = 10;
12.    try {
13.        $age = intval($age);
14.        if($age > 60) {
15.            throw new zyfException($age);
16.        }
17.
18.    } catch (zyfException $e) {
19.        echo $e->errorzyfMessage();
20.
21.    }
22.?>

  当然也可以在catch中再抛出异常给上层:

1.<?php
2.    $age = 100;
3.    try {
4.        try {
5.            $age = intval($age);
6.            if($age > 60) {
7.                throw new Exception($age);
8.            }
9.
10.        } catch (Exception $e) {
11.            throw new zyfException($age);
12.
13.        }
14.
15.    } catch (zyfException $e) {
16.        echo $e->errorzyfMessage();
17.    }
18.?>

六、PHP7中的异常处理

  现在写PHP必须考虑版本情况,上面的写法在PHP7中大部分都能实现,但是也会有不同点,在PHP7更新中有一条:更多的Error变为可捕获的Exception,现在的PHP7实现了一个全局的throwable接口,原来老的Exception和其中一部分Error实现了这个接口(interface),PHP7中更多的Error变为可捕获的Exception返回给捕捉器,这样其实和前面提到的扩展try-catch影响范围一样,但是如果不捕获则还是按照Error对待,看下面两个:

1.<?php
2.    try {
3.        test();
4.
5.    } catch(Throwable $e) {
6.        echo $e->getMessage() . ' zyf';
7.    }
8.
9.    try {
10.        test();
11.
12.    } catch(Error $e) {<span style="font-size:14px;"></span>
13.        echo $e->getMessage() . ' zyf';
14.    }
15.?>

  因为PHP7实现了throwable接口,那么就可以使用第一个这种方式来捕获异常。又因为部分Error实现了接口,并且更多的Error变为可捕获的Exception,那么就可以使用第二种方式来捕获异常。下面是在网上找的PHP7的异常层次树:
Throwable
  Exception 异常
    …
  Error 错误
    ArithmeticError 算数错误
      DivisionByZeroError 除数为0的错误
    AssertionError 声明错误
    ParseError 解析错误
    TypeError 类型错误

就写到这吧,写得手疼,关于错误和异常处理的大致就写这么多,有什么错误请在评论中给出,多谢大家。

本文转载自 http://www.zhaoyafei.cn/content.html?id=170641242245

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值