资讯专栏INFORMATION COLUMN

如何处理 PHP 的错误与异常(笔记)

kyanag / 2156人阅读

摘要:又或者反过来,把错误当成异常来处理。当然,我猜它的目的,应该也是为了能实现错误与异常之间优雅转换而添加的。至此,错误与异常的学习基本完毕。

这话题已经没有什么新意了,这里只是做做笔记,作为思路的一种整理,也以便后续忘了可以回来这里查找。

错误

以下是 PHP 最常见的几种错误:

// E_NOTICE
echo $a;

// E_WARNING
echo 100 / 0;

class Sample
{
    public function method()
    {
        //not static method
    }
}

// E_STRICT
Sample::method();

// E_ERROR
new Dummy();

运行上面代码,页面输出以下信息:

Notice: Undefined variable: a in D:errors-exceptionsdemo4.php on line 6

Warning: Division by zero in D:errors-exceptionsdemo4.php on line 9

Strict Standards: Non-static method Sample::method() should not be called statically in D:errors-exceptionsdemo4.php on line 20

Fatal error: Class "Dummy" not found in D:errors-exceptionsdemo4.php on line 23

在生产环境下,是不允许把错误信息输出到页面的。

怎么办?关闭错误输出

ini_set("display_errors", 0);

此时,刷新页面,页面将不会报任何错误。页面一片空白,或者显示 500 错误。

这也不是我们希望的,虽然不把错误输出到页面,但是这些错误我们是希望把它们都收集起来,写到日志里面,以便开发人员能够不断改进代码,排查错误。

怎么办?自定义错误处理

set_error_handler(function($errno, $errstr, $errfile, $errline) {

    //在这里对错误进行处理

    echo $errstr . "
"; });

运行上面的代码,页面输出:

Undefined variable: a
Division by zero
Non-static method Sample::method() should not be called statically

很奇怪,不是应该输出 4 个错误吗?怎么 Fatal error 没有捕捉到。查看 set_error_handler 帮助文档,我们发现,它有以下描述:

以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。

噢~,原来是这样,原来 set_error_handler 方法是不能捕捉 E_ERROR 错误的(上面的 Fatal error)。那么这类错误,我们是不能放过的,也必须捕捉到,并做适当的处理。

怎么办? 利用 register_shutdown_function 函数

register_shutdown_function(function() {

    if(is_null($e = error_get_last()) === false) {

        echo $e["message"] . "
"; } });

此时,再运行上面的例子,页面会输出:

Undefined variable: a
Division by zero
Non-static method Sample::method() should not be called statically
Class "Dummy" not found

很好,四种错误,我们都成功捕捉了,并按照我们自己的方式进行了输出。

到目前为止,应该说能捕捉的 PHP 错误,我们都捕捉到了,当然还有一些错误,压根就没有办法捕捉。比如语法错误,这类错误是在 PHP 引擎对即将执行的文件编译期间的错误。


像上面这种错误,程序就没有办法捕捉了。只要程序是经过测试的,一般不会在生产环境出现此类错误,所以也不用过于但心。

最后注:register_shutdown_function 函数会在任何导致页面退出的时候,会被调用。比如发生了致命错误、使用 exit() 函数、又或者是页面执行完毕了,都会触发该函数。利用这个特征,我们可以在页面退出时,获取到最后一个错误,然后进行记录。这个的 “最后一个错误” 往往是致命错误,原因很简单:因为一旦错误被 set_error_handler 捕捉到了,那么 register_shutdown_function 将捕捉不了。而 前者捕捉不了的,才会被后者捕捉。

异常

在 PHP 中,所以异常的基类都是 Exception。异常应该说是在 PHP 后来引入了面向对象的概念后,才有的产物。那么说,PHP 原来只抛错误,却没有异常的概念了。但现在,异常的使用已经非常广泛了,我们有必要学习一下。

好,我们来制造一些异常:

new PDO("mysql:dbname=testdb;host=127.0.0.1", "root", "wrong_passwd");

//运行结果
Fatal error: Uncaught exception "PDOException" with message "SQLSTATE[HY000] [1045] Access denied for user "root"@"localhost" (using password: YES)" in D:errors-exceptionsdemo6.php:6 Stack trace: #0 D:errors-exceptionsdemo6.php(6): PDO->__construct("mysql:dbname=te...", "root", "wrong_passwd") #1 {main} thrown in D:errors-exceptionsdemo6.php on line 6

再制造一个:

throw new Exception("我是异常");

//运行结果
Fatal error: Uncaught exception "Exception" with message "我是异常" in D:errors-exceptionsdemo6.php:9 Stack trace: #0 {main} thrown in D:errors-exceptionsdemo6.php on line 9

在生产环境下,页面直接输出这些异常,同样是不优雅的,那么我们同样可以像关闭错误输出一样,关闭异常的输出:

ini_set("display_errors", 0);

同样地,异常虽然不显示出来了,但是我们需要记录并处理这些异常。

怎样做? 使用 set_exception_handler 函数

set_exception_handler(function($exception) {

    //在这里,统一处理异常
    
    echo get_class($exception) .": ". $exception->getMessage();
});

此时,再运行页面,会输出以下信息:

PDOException: SQLSTATE[HY000] [1045] Access denied for user "root"@"localhost" (using password: YES)
小结

在我刚开始学习 PHP 的时候,的确被它的错误和异常困扰了许久。最开始,我甚至不会用异常。实际上,在现代的 PHP 里面,我们基本上可以完全控制它的异常和错误。只需要分开处理就可以了。

扩展:我们在实际项目中捕捉并处理异常和错误的时候,往往是把两者合二为一。就是说把异常也当成错误来处理。又或者反过来,把错误当成异常来处理。

下面,就让我们一起看看,如何把两者相互转换:

把异常转换为错误处理

";
}

//自定义异常处理
function exceptionHandler($exception) {
    errorHandler(
        $exception->getCode(),
        $exception->getMessage(),
        $exception->getFile(),
        $exception->getLine()
    );
}

//自定义致命错误处理
function shutdownHandler() {
    if(is_null($e = error_get_last()) === false) {
        errorHandler(
            $e["type"],
            $e["message"],
            $e["file"],
            $e["line"]
        );
    }
}

不过这样处理法,会导致异常的堆栈信息“丢失”,意思是无法处理这些堆栈信息了。当然是有办法处理的了,具体请看 demo3.php。

再看看,如何把错误转换为异常 (注意:以下代码仅为了演示其原理,代码本身并非完全合理)

set_error_handler("errorHandler");

function errorHandler($errno, $errstr, $errfile, $errline) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
};

大家看到,我们使用了 ErrorException 这个类,这是 PHP 后来才引入的,叫作错误异常。当然,我猜它的目的,应该也是为了能实现错误与异常之间优雅转换而添加的。

至此,错误与异常的学习基本完毕。最后推荐看看一个网站有关于对 PHP 错误与异常的介绍,尤其是对异常的一些行为的说明,都是值得注意的,网站在这里:PHP - Error & Exception Handling

参考文献

PHP在什么时候应该使用异常处理(Exception)?

PHP Trick: Catching fatal errors (E_ERROR) with a custom error handler

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30299.html

相关文章

  • 何处Express异常

    摘要:处理异常是编程非常重要的一点。我们的程序依赖于第三方服务数据库以及我们的用户,一切都不可预料。为了处理这些错误,需要添加一个中间件,它有个参数这样,我们就可以使用中间件统一处理错误了。 译者按:根据墨菲定律:有可能出错的事情,就会出错。那么,既然代码必然会出错,我们就应该处理好异常。 原文: How to handle errors in Express 译者:Fundebug ...

    justjavac 评论0 收藏0
  • 《Java编程思想》笔记12.通过异常错误

    摘要:一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。这种在编译时被强制检查的异常称为被检查的异常。通过获取原始异常。构造器对于在构造阶段可能会抛出异常,并要求清理的类,最安全的做法是使用嵌套的子句。 点击进入我的博客 Java异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更自信:你的应用中没有未处理的错误。 12.1 概念 异...

    Vultr 评论0 收藏0
  • 异常机制详解

    摘要:当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。不可查异常编译器不要求强制处置的异常包括运行时异常与其子类和错误。 目录介绍 1.什么是异常 2.异常 2.1 异常的概述和分类【了解】 2.2 JVM默认是如何处理异常的【理解】 2.3 异常处理的两种方式【理解】 2.4 try...catch的方式处理异常【掌握】 2.5 编译期异常和运行期异常的区别【理解】...

    wanghui 评论0 收藏0
  • Java中异常

    摘要:对异常的处理方法是打印异常的跟踪栈信息并终止程序运行。应尽量对异常进行适当的处理,而不是简单的将异常跟踪栈信息打印出来。 一、异常概述 开发者都希望所有错误都能在编译阶段被发现,就是试图在运行程序之前排除所有错误,但这是不现实的,余下问题必须在运行期间得到解决。 Java将异常分为两种:CheckedException和RuntimeException。其中,CheckedExcept...

    wemall 评论0 收藏0
  • Java编程基础20——异常&IO(File类)

    摘要:异常异常的概述和分类异常的概述异常就是程序在运行过程中出现的错误。运行时异常就是程序员所犯的错误,需要回来修改代码。获取异常类名和异常信息,返回字符串。如果路径名不同,就是改名并剪切。删除注意事项中的删除不走回收站。 1_异常(异常的概述和分类) A:异常的概述 异常就是Java程序在运行过程中出现的错误。 B:异常的分类 通过API查看Throwable Error 服务...

    church 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<