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

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


当前回答

我开发这个函数是为了使“沙盒”代码能够导致致命错误。由于从闭包register_shutdown_function抛出的异常不会从预致命错误调用堆栈中触发,因此我被迫在此函数之后退出,以提供统一的使用方法。

function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally )
{
    $finished = FALSE;
    register_shutdown_function( function() use ( &$finished, $catch, $finally ) {
        if( ! $finished ) {
            $finished = TRUE;
            print "EXPLODE!".PHP_EOL;
            if( $catch ) {
                superTryCatchFinallyAndExit( function() use ( $catch ) {
                    $catch( new Exception( "Fatal Error!!!" ) );
                }, NULL, $finally );                
            } else {
                $finally();                
            }
        }
    } );
    try {
        $try();
    } catch( Exception $e ) {
        if( $catch ) {
            try {
                $catch( $e );
            } catch( Exception $e ) {}
        }
    }
    $finished = TRUE;
    $finally();
    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

您不能捕获/处理致命错误,但可以记录/报告它们。 为了快速调试,我修改了这段简单代码的一个答案

function __fatalHandler()
{
    $error = error_get_last();

    // Check if it's a core/fatal error, otherwise it's a normal shutdown
    if ($error !== NULL && in_array($error['type'],
        array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING,
              E_COMPILE_ERROR, E_COMPILE_WARNING,E_RECOVERABLE_ERROR))) {

        echo "<pre>fatal error:\n";
        print_r($error);
        echo "</pre>";
        die;
    }
}

register_shutdown_function('__fatalHandler');

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

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

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

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

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

使用register_shutdown_function记录致命错误,这需要PHP 5.2+:

register_shutdown_function( "fatal_handler" );

function fatal_handler() {
    $errfile = "unknown file";
    $errstr  = "shutdown";
    $errno   = E_CORE_ERROR;
    $errline = 0;

    $error = error_get_last();

    if($error !== NULL) {
        $errno   = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr  = $error["message"];

        error_mail(format_error( $errno, $errstr, $errfile, $errline));
    }
}

必须定义error_mail和format_error函数。例如:

function format_error( $errno, $errstr, $errfile, $errline ) {
    $trace = print_r( debug_backtrace( false ), true );

    $content = "
    <table>
        <thead><th>Item</th><th>Description</th></thead>
        <tbody>
            <tr>
                <th>Error</th>
                <td><pre>$errstr</pre></td>
            </tr>
            <tr>
                <th>Errno</th>
                <td><pre>$errno</pre></td>
            </tr>
            <tr>
                <th>File</th>
                <td>$errfile</td>
            </tr>
            <tr>
                <th>Line</th>
                <td>$errline</td>
            </tr>
            <tr>
                <th>Trace</th>
                <td><pre>$trace</pre></td>
            </tr>
        </tbody>
    </table>";
    return $content;
}

使用Swift Mailer编写error_mail函数。

参见:

php_errormsg美元 预定义常量

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

我在我的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();
    }
}