我可以使用set_error_handler()来捕获大多数PHP错误,但它不适用于致命(E_ERROR)错误,例如调用不存在的函数。是否有其他方法来捕捉这些错误?

我试图调用mail()为所有错误和运行PHP 5.2.3。


当前回答

由于这里的大多数答案都是不必要的啰嗦,下面是我投票最多的答案的不丑陋版本:

function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) {
    //Do stuff: mail, log, etc
}

function fatalHandler() {
    $error = error_get_last();
    if($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}

set_error_handler("errorHandler")
register_shutdown_function("fatalHandler");

其他回答

我刚刚想出了这个解决方案(PHP 5.2.0+):

function shutDownFunction() {
    $error = error_get_last();
     // Fatal error, E_ERROR === 1
    if ($error['type'] === E_ERROR) {
         // Do your stuff
    }
}
register_shutdown_function('shutDownFunction');

不同的错误类型在预定义常量中定义。

在某些情况下,即使是致命的错误也应该被捕获(你可能需要在优雅地退出之前做一些清理工作,而不是直接死亡..)。

我在我的CodeIgniter应用程序中实现了一个pre_system钩子,这样我就可以通过电子邮件得到我的致命错误,这帮助我找到没有报告的错误(或在修复后报告的错误,因为我已经知道了它们:))。

Sendemail检查错误是否已经报告,这样它就不会多次向您发送已知错误的垃圾邮件。

class PHPFatalError {

    public function setHandler() {
        register_shutdown_function('handleShutdown');
    }
}

function handleShutdown() {
    if (($error = error_get_last())) {
        ob_start();
        echo "<pre>";
        var_dump($error);
        echo "</pre>";
        $message = ob_get_clean();
        sendEmail($message);
        ob_start();
        echo '{"status":"error","message":"Internal application error!"}';
        ob_flush();
        exit();
    }
}

在PHP 7或更高版本中,致命错误或可恢复的致命错误现在会抛出错误实例。像任何其他异常一样,Error对象可以使用try/catch块捕获。

例子:

<?php
$variable = 'not an object';

try {
    $variable->method(); // Throws an Error object in PHP 7 or higger.
} catch (Error $e) {
    // Handle error
    echo $e->getMessage(); // Call to a member function method() on string
}

https://3v4l.org/67vbk

或者您可以使用Throwable接口来捕获所有异常。

例子:

<?php
    try {
        undefinedFunctionCall();
    } catch (Throwable $e) {
        // Handle error
        echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
    }

https://3v4l.org/Br0MG

欲了解更多信息:http://php.net/manual/en/language.errors.php7.php

PHP没有提供常规的方法来捕捉和恢复致命错误。这是因为在发生致命错误后通常不应恢复处理。字符串匹配输出缓冲区(正如PHP.net上描述的技术的原始帖子所建议的那样)绝对是不明智的。这是不可靠的。

从错误处理程序方法中调用mail()函数也有问题。如果你有很多错误,你的邮件服务器就会有大量的工作,你就会发现自己有一个杂乱的收件箱。为了避免这种情况,可以考虑运行cron定期扫描错误日志并相应地发送通知。您可能还想研究一下系统监控软件,比如Nagios。


关于注册一个shutdown函数:

的确,您可以注册一个shutdown函数,这是一个很好的答案。

这里的重点是,我们通常不应该试图从致命错误中恢复,特别是不应该对输出缓冲区使用正则表达式。我是在回复被接受的答案,它链接到php.net上的一个建议,这个建议已经被更改或删除了。

该建议是在异常处理期间对输出缓冲区使用正则表达式,并且在出现致命错误的情况下(通过匹配您可能期望的任何配置错误文本检测到),尝试进行某种恢复或继续处理。这不是一个推荐的做法(我相信这就是为什么我也找不到最初的建议。不是我忽略了它,就是php社区否定了它)。

It might be worth noting that the more recent versions of PHP (around 5.1) seem to call the shutdown function earlier, before the output buffering callback is envoked. In version 5 and earlier, that order was the reverse (the output buffering callback was followed by the shutdown function). Also, since about 5.0.5 (which is much earlier than the questioner's version 5.2.3), objects are unloaded well before a registered shutdown function is called, so you won't be able to rely on your in-memory objects to do much of anything.

因此,注册一个关闭函数是可以的,但是应该由关闭函数执行的任务可能仅限于少数温和的关闭过程。

这里的关键是给那些偶然遇到这个问题并在最初接受的答案中看到建议的人的一些智慧之语。不要正则化你的输出缓冲区。

PHP有可捕获的致命错误。它们被定义为E_RECOVERABLE_ERROR。PHP手册将E_RECOVERABLE_ERROR描述为:

可捕捉的致命错误。它表示可能发生了危险的错误,但没有使引擎处于不稳定状态。如果用户定义的句柄没有捕捉到错误(参见set_error_handler()),应用程序将中止,因为它是一个E_ERROR。

您可以通过使用set_error_handler()并检查E_RECOVERABLE_ERROR来“捕获”这些“致命”错误。我发现当这个错误被捕获时抛出一个异常很有用,然后你可以使用try/catch。

这个问题和答案提供了一个有用的例子:如何在PHP类型提示中捕获“可捕获的致命错误”?

然而,E_ERROR错误可以处理,但不能恢复,因为引擎处于不稳定状态。