我可以使用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();
}

其他回答

好吧,似乎可以用其他方式捕获致命错误:)

ob_start('fatal_error_handler');

function fatal_error_handler($buffer){
    $error = error_get_last();
    if($error['type'] == 1){
        // Type, message, file, line
        $newBuffer='<html><header><title>Fatal Error </title></header>
                      <style>
                    .error_content{
                        background: ghostwhite;
                        vertical-align: middle;
                        margin:0 auto;
                        padding: 10px;
                        width: 50%;
                     }
                     .error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;}
                     .error_content ul li{ background: none repeat scroll 0 0 FloralWhite;
                                border: 1px solid AliceBlue;
                                display: block;
                                font-family: monospace;
                                padding: 2%;
                                text-align: left;
                      }
                      </style>
                      <body style="text-align: center;">
                        <div class="error_content">
                             <label >Fatal Error </label>
                             <ul>
                               <li><b>Line</b> ' . $error['line'] . '</li>
                               <li><b>Message</b> ' . $error['message'] . '</li>
                               <li><b>File</b> ' . $error['file'] . '</li>
                             </ul>

                             <a href="javascript:history.back()"> Back </a>
                        </div>
                      </body></html>';

        return $newBuffer;
    }
    return $buffer;
}

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

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

下面是一个获取当前error_handler method =)的小技巧

<?php
    register_shutdown_function('__fatalHandler');

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

        // Check if it's a core/fatal error. Otherwise, it's a normal shutdown
        if($error !== NULL && $error['type'] === E_ERROR) {

            // It is a bit hackish, but the set_exception_handler
            // will return the old handler
            function fakeHandler() { }

            $handler = set_exception_handler('fakeHandler');
            restore_exception_handler();
            if($handler !== null) {
                call_user_func(
                    $handler,
                    new ErrorException(
                        $error['message'],
                        $error['type'],
                        0,
                        $error['file'],
                        $error['line']));
            }
            exit;
        }
    }
?>

我还想提醒你,如果你打电话来

<?php
    ini_set('display_errors', false);
?>

PHP停止显示错误。否则,错误文本将先于错误处理程序发送给客户端。

在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有可捕获的致命错误。它们被定义为E_RECOVERABLE_ERROR。PHP手册将E_RECOVERABLE_ERROR描述为:

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

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

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

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