简体   繁体   English

如何捕获 PHP 致命(`E_ERROR`)错误?

[英]How do I catch a PHP fatal (`E_ERROR`) error?

I can use set_error_handler() to catch most PHP errors, but it doesn't work for fatal ( E_ERROR ) errors, such as calling a function that doesn't exist.我可以使用set_error_handler()来捕获大多数 PHP 错误,但它不适用于致命 ( E_ERROR ) 错误,例如调用不存在的函数。 Is there another way to catch these errors?还有其他方法可以捕获这些错误吗?

I am trying to call mail() for all errors and am running PHP 5.2.3.我正在尝试为所有错误调用mail() ,并且正在运行 PHP 5.2.3。

Log fatal errors using the register_shutdown_function , which requires PHP 5.2+:使用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));
    }
}

You will have to define the error_mail and format_error functions.您必须定义error_mailformat_error函数。 For example:例如:

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;
}

Use Swift Mailer to write the error_mail function.使用Swift Mailer编写error_mail函数。

See also:也可以看看:

I just came up with this solution (PHP 5.2.0+):我刚刚想出了这个解决方案(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');

Different error types are defined at Predefined Constants .Predefined Constants中定义了不同的错误类型。

PHP doesn't provide conventional means for catching and recovering from fatal errors. PHP 不提供用于捕获和从致命错误中恢复的传统方法。 This is because processing should not typically be recovered after a fatal error.这是因为在发生致命错误后通常不应恢复处理。 String matching an output buffer (as suggested by the original post the technique described on PHP.net) is definitely ill-advised.匹配输出缓冲区的字符串(如 PHP.net 上描述的技术的原始帖子所建议的)绝对是不明智的。 It's simply unreliable.这简直是​​不可靠的。

Calling the mail() function from within an error handler method prove to be problematic, too.从错误处理程序方法中调用 mail() 函数也证明是有问题的。 If you had a lot of errors, your mail server would be loaded with work, and you could find yourself with a gnarly inbox.如果你有很多错误,你的邮件服务器就会充满工作,你会发现自己的收件箱很粗糙。 To avoid this, you might consider running a cron to scan error logs periodically and send notifications accordingly.为避免这种情况,您可以考虑运行 cron 定期扫描错误日志并相应地发送通知。 You might also like to look into system monitoring software, such as Nagios .您可能还想研究系统监控软件,例如Nagios


To speak to the bit about registering a shutdown function:谈谈关于注册关闭功能的一点:

It's true that you can register a shutdown function, and that's a good answer.确实可以注册关机功能,这是一个很好的答案。

The point here is that we typically shouldn't try to recover from fatal errors, especially not by using a regular expression against your output buffer.这里的重点是,我们通常不应该尝试从致命错误中恢复,尤其是不要对输出缓冲区使用正则表达式。 I was responding to the accepted answer , which linked to a suggestion on php.net which has since been changed or removed.我正在回应已接受的答案,该答案与 php.net 上的建议相关联,该建议已被更改或删除。

That suggestion was to use a regex against the output buffer during exception handling, and in the case of a fatal error (detected by the matching against whatever configured error text you might be expecting), try to do some sort of recovery or continued processing.该建议是在异常处理期间对输出缓冲区使用正则表达式,并且在发生致命错误的情况下(通过与您可能期望的任何配置错误文本进行匹配来检测),尝试进行某种恢复或继续处理。 That would not be a recommended practice (I believe that's why I can't find the original suggestion, too. I'm either overlooking it, or the php community shot it down).这不是推荐的做法(我相信这就是为什么我也找不到原始建议的原因。我要么忽略了它,要么 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.可能值得注意的是,较新版本的 PHP(大约 5.1)似乎在调用输出缓冲回调之前更早地调用了关闭函数。 In version 5 and earlier, that order was the reverse (the output buffering callback was followed by the shutdown function).在版本 5 及更早版本中,该顺序是相反的(输出缓冲回调之后是关闭函数)。 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.此外,由于大约 5.0.5(比提问者的版本 5.2.3 早得多),在调用注册的关闭函数之前很好地卸载了对象,因此您将无法依赖内存中的对象来执行很多东西。

So registering a shutdown function is fine, but the sort of tasks that ought to be performed by a shutdown function are probably limited to a handful of gentle shutdown procedures.所以注册关闭函数是好的,但是应该由关闭函数执行的任务可能仅限于少数温和的关闭程序。

The key take-away here is just some words of wisdom for anyone who stumbles upon this question and sees the advice in the originally accepted answer.对于任何偶然发现这个问题并在最初接受的答案中看到建议的人来说,这里的关键要点只是一些智慧之词。 Don't regex your output buffer.不要正则表达式你的输出缓冲区。

Fatal errors or recoverable fatal errors now throw instances of Error in PHP 7 or higher versions .致命错误或可恢复的致命错误现在会在PHP 7 或更高版本中抛出Error实例。 Like any other exceptions, Error objects can be caught using a try/catch block.与任何其他异常一样,可以使用try/catch块捕获Error对象。

Example:例子:

<?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 https://3v4l.org/67vbk

Or you can use Throwable interface to catch all exceptions.或者您可以使用Throwable接口来捕获所有异常。

Example:例子:

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

https://3v4l.org/Br0MG https://3v4l.org/Br0MG

For more information: http://php.net/manual/en/language.errors.php7.php更多信息: http : //php.net/manual/en/language.errors.php7.php

Well, it seems possible to catch fatal errors some other way :)好吧,似乎可以通过其他方式捕获致命错误:)

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;
}

You can't catch/handle fatal errors, but you can log/report them.您无法捕获/处理致命错误,但可以记录/报告它们。 For quick debugging I modified one answer to this simple code为了快速调试,我修改了这个简单代码的一个答案

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');

I developed a way to catch all error types in PHP (almost all)!我开发了一种方法来捕获 PHP 中的所有错误类型(几乎所有)! I have no sure about E_CORE_ERROR (I think will not works only for that error)!我不确定 E_CORE_ERROR (我认为不仅适用于该错误)! But, for other fatal errors (E_ERROR, E_PARSE, E_COMPILE...) works fine using only one error handler function!但是,对于其他致命错误(E_ERROR、E_PARSE、E_COMPILE...),仅使用一个错误处理函数就可以正常工作! There goes my solution:这是我的解决方案:

Put this following code on your main file (index.php):将以下代码放在您的主文件 (index.php) 中:

<?php
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

    define('ENV', 'dev');

    // Custom error handling vars
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);

    register_shutdown_function('shut');

    set_error_handler('handler');

    // Function to catch no user error handler function errors...
    function shut(){

        $error = error_get_last();

        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }

    }

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

        switch ($errno){

            case E_ERROR: // 1 //
                $typestr = 'E_ERROR'; break;
            case E_WARNING: // 2 //
                $typestr = 'E_WARNING'; break;
            case E_PARSE: // 4 //
                $typestr = 'E_PARSE'; break;
            case E_NOTICE: // 8 //
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR: // 16 //
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING: // 32 //
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR: // 64 //
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_CORE_WARNING: // 128 //
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR: // 256 //
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING: // 512 //
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: // 1024 //
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT: // 2048 //
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: // 8192 //
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: // 16384 //
                $typestr = 'E_USER_DEPRECATED'; break;
        }

        $message =
            '<b>' . $typestr .
            ': </b>' . $errstr .
            ' in <b>' . $errfile .
            '</b> on line <b>' . $errline .
            '</b><br/>';

        if(($errno & E_FATAL) && ENV === 'production'){

            header('Location: 500.html');
            header('Status: 500 Internal Server Error');

        }

        if(!($errno & ERROR_REPORTING))
            return;

        if(DISPLAY_ERRORS)
            printf('%s', $message);

        //Logging error on php file error log...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    }

    ob_start();

    @include 'content.php';

    ob_end_flush();
?>

You cannot throw an exception inside a registered shutdown function like that:您不能在注册的关闭函数中抛出异常,如下所示:

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           throw new Exception("fatal error");
        }
    }

    try {
        $x = null;
        $x->method()
    } catch(Exception $e) {
        # This won't work
    }
?>

But you can capture and redirect request to another page.但是您可以捕获请求并将其重定向到另一个页面。

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           # Report the event, send email, etc.
           header("Location: http://localhost/error-capture");
           # From /error-capture. You can use another
           # redirect, to e.g. the home page
        }
    }
    register_shutdown_function('shutdown');

    $x = null;
    $x->method()
?>

If you are using PHP >= 5.1.0 Just do something like this with the ErrorException class:如果您使用的是 PHP >= 5.1.0 只需使用 ErrorException 类执行以下操作:

<?php
    // Define an error handler
    function exception_error_handler($errno, $errstr, $errfile, $errline ) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }

    // Set your error handler
    set_error_handler("exception_error_handler");

    /* Trigger exception */
    try
    {
        // Try to do something like finding the end of the internet
    }
    catch(ErrorException $e)
    {
        // Anything you want to do with $e
    }
?>

Nice solution found in Zend Framework 2:在 Zend Framework 2 中找到了不错的解决方案:

/**
 * ErrorHandler that can be used to catch internal PHP errors
 * and convert to an ErrorException instance.
 */
abstract class ErrorHandler
{
    /**
     * Active stack
     *
     * @var array
     */
    protected static $stack = array();

    /**
     * Check if this error handler is active
     *
     * @return bool
     */
    public static function started()
    {
        return (bool) static::getNestedLevel();
    }

    /**
     * Get the current nested level
     *
     * @return int
     */
    public static function getNestedLevel()
    {
        return count(static::$stack);
    }

    /**
     * Starting the error handler
     *
     * @param int $errorLevel
     */
    public static function start($errorLevel = \E_WARNING)
    {
        if (!static::$stack) {
            set_error_handler(array(get_called_class(), 'addError'), $errorLevel);
        }

        static::$stack[] = null;
    }

    /**
     * Stopping the error handler
     *
     * @param  bool $throw Throw the ErrorException if any
     * @return null|ErrorException
     * @throws ErrorException If an error has been catched and $throw is true
     */
    public static function stop($throw = false)
    {
        $errorException = null;

        if (static::$stack) {
            $errorException = array_pop(static::$stack);

            if (!static::$stack) {
                restore_error_handler();
            }

            if ($errorException && $throw) {
                throw $errorException;
            }
        }

        return $errorException;
    }

    /**
     * Stop all active handler
     *
     * @return void
     */
    public static function clean()
    {
        if (static::$stack) {
            restore_error_handler();
        }

        static::$stack = array();
    }

    /**
     * Add an error to the stack
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     * @return void
     */
    public static function addError($errno, $errstr = '', $errfile = '', $errline = 0)
    {
        $stack = & static::$stack[count(static::$stack) - 1];
        $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack);
    }
}

This class allows you to start the specific ErrorHandler sometimes if you need it.如果需要,此类有时允许您启动特定的ErrorHandler And then you can also stop the Handler.然后你也可以停止处理程序。

Use this class eg like this:像这样使用这个类:

ErrorHandler::start(E_WARNING);
$return = call_function_raises_E_WARNING();

if ($innerException = ErrorHandler::stop()) {
    throw new Exception('Special Exception Text', 0, $innerException);
}

// or
ErrorHandler::stop(true); // directly throws an Exception;

Link to the full class code:链接到完整的类代码:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


A maybe better solution is that one from Monolog :一个可能更好的解决方案是Monolog中的一个:

Link to the full class code: 链接到完整的类代码:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

It can also handle FATAL_ERRORS using the register_shutdown_function function.它还可以使用register_shutdown_function函数处理 FATAL_ERRORS。 According to this class a FATAL_ERROR is one of the following array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR) .根据此类, FATAL_ERROR 是以下array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR)

 class ErrorHandler { // [...] public function registerExceptionHandler($level = null, $callPrevious = true) { $prev = set_exception_handler(array($this, 'handleException')); $this->uncaughtExceptionLevel = $level; if ($callPrevious && $prev) { $this->previousExceptionHandler = $prev; } } public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1) { $prev = set_error_handler(array($this, 'handleError'), $errorTypes); $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); if ($callPrevious) { $this->previousErrorHandler = $prev ?: true; } } public function registerFatalHandler($level = null, $reservedMemorySize = 20) { register_shutdown_function(array($this, 'handleFatalError')); $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); $this->fatalLevel = $level; } // [...] }

I need to handle fatal errors for production to instead show a static styled 503 Service Unavailable HTML output.我需要处理生产的致命错误,而不是显示静态样式的503 服务不可用HTML 输出。 This is surely a reasonable approach to "catching fatal errors".这无疑是“捕获致命错误”的合理方法。 This is what I've done:这就是我所做的:

I have a custom error handling function "error_handler" which will display my "503 service unavailable" HTML page on any E_ERROR, E_USER_ERROR, etc. This will now be called on the shutdown function, catching my fatal error,我有一个自定义错误处理函数“error_handler”,它将在任何 E_ERROR、E_USER_ERROR 等上显示我的“503 服务不可用”HTML 页面。现在将在关闭函数上调用它,捕获我的致命错误,

function fatal_error_handler() {

    if (@is_array($e = @error_get_last())) {
        $code = isset($e['type']) ? $e['type'] : 0;
        $msg = isset($e['message']) ? $e['message'] : '';
        $file = isset($e['file']) ? $e['file'] : '';
        $line = isset($e['line']) ? $e['line'] : '';
        if ($code>0)
            error_handler($code, $msg, $file, $line);
    }
}
set_error_handler("error_handler");
register_shutdown_function('fatal_error_handler');

in my custom error_handler function, if the error is E_ERROR, E_USER_ERROR, etc. I also call @ob_end_clean();在我的自定义 error_handler 函数中,如果错误是 E_ERROR、E_USER_ERROR 等,我也调用@ob_end_clean(); to empty the buffer, thus removing PHP's "fatal error" message.清空缓冲区,从而删除 PHP 的“致命错误”消息。

Take important note of the strict isset() checking and @ silencing functions since we don't want our error_handler scripts to generate any errors.以严格的isset的重要说明()检查和@沉默的功能,因为我们不希望我们的error_handler脚本产生任何错误。

In still agreeing with keparo, catching fatal errors does defeat the purpose of "FATAL error" so it's not really intended for you to do further processing.仍然同意 keparo,捕获致命错误确实违背了“致命错误”的目的,因此它并不是真正打算让您进行进一步处理。 Do not run any mail() functions in this shutdown process as you will certainly back up the mail server or your inbox.不要在此关闭过程中运行任何 mail() 函数,因为您肯定会备份邮件服务器或收件箱。 Rather log these occurrences to file and schedule a cron job to find these error.log files and mail them to administrators.而是将这些事件记录到文件中并安排一个cron作业来查找这些error.log文件并将它们邮寄给管理员。

Here is just a nice trick to get the current error_handler method =)这是获取当前 error_handler 方法的一个不错的技巧 =)

<?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;
        }
    }
?>

Also I want to note that if you call我还要注意,如果你打电话

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

PHP stops displaying the error. PHP 停止显示错误。 Otherwise, the error text will be send to the client prior to your error handler.否则,错误文本将在您的错误处理程序之前发送到客户端。

PHP has catchable fatal errors. PHP 有可捕获的致命错误。 They are defined as E_RECOVERABLE_ERROR.它们被定义为 E_RECOVERABLE_ERROR。 The PHP manual describes an E_RECOVERABLE_ERROR as: PHP 手册将 E_RECOVERABLE_ERROR 描述为:

Catchable fatal error.可捕获的致命错误。 It indicates that a probably dangerous error occured, but did not leave the Engine in an unstable state.它表示发生了可能危险的错误,但并未使引擎处于不稳定状态。 If the error is not caught by a user defined handle (see also set_error_handler() ), the application aborts as it was an E_ERROR.如果错误没有被用户定义的句柄捕获(另见set_error_handler() ),应用程序将中止,因为它是一个 E_ERROR。

You can "catch" these "fatal" errors by using set_error_handler() and checking for E_RECOVERABLE_ERROR.您可以通过使用set_error_handler()并检查 E_RECOVERABLE_ERROR 来“捕捉”这些“致命”错误。 I find it useful to throw an Exception when this error is caught, then you can use try/catch.我发现在捕获此错误时抛出异常很有用,然后您可以使用 try/catch。

This question and answer provides a useful example: How can I catch a "catchable fatal error" on PHP type hinting?此问答提供了一个有用的示例: 如何在 PHP 类型提示上捕获“可捕获的致命错误”?

E_ERROR errors, however, can be handled, but not recovered from as the engine is in an unstable state.但是,E_ERROR 错误可以处理,但无法恢复,因为引擎处于不稳定状态。

Since most answers here are unnecesarily verbose, here's my non-ugly version of the top voted answer:由于这里的大多数答案都不必要地冗长,这是我投票最多的答案的非丑陋版本:

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");

Not really.并不真地。 Fatal errors are called that, because they are fatal.致命错误被称为,因为它们是致命的。 You can't recover from them.你无法从他们那里恢复过来。

There are certain circumstances in which even fatal errors should be caught (you might need to do some clean up before exiting gracefully and don't just die..).在某些情况下,甚至应该捕获致命错误(您可能需要在优雅退出之前进行一些清理,而不要只是死掉......)。

I have implemented a pre_system hook in my CodeIgniter applications so that I can get my fatal errors through emails, and this helped me finding bugs that were not reported (or were reported after they were fixed, as I already knew about them :)).我在我的CodeIgniter应用程序中实现了一个 pre_system 挂钩,以便我可以通过电子邮件获取我的致命错误,这有助于我找到未报告的错误(或在修复后报告的错误,因为我已经知道它们:))。

Sendemail checks if the error has already been reported so that it does not spam you with known errors multiple times. 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();
    }
}

I developed this function to make it possible to "sandbox" code that could cause a fatal error.我开发了这个函数,使“沙箱”代码成为可能,这可能会导致致命错误。 Since exceptions thrown from the closure register_shutdown_function don't get emitted from the pre-fatal error call stack, I'm forced to exit after this function to provide a uniform way of using it.由于从关闭register_shutdown_function抛出的异常不会从 pre-fatal 错误调用堆栈中发出,我被迫在此函数之后退出,以提供一种统一的使用方式。

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

I wrote up a Wiki-style Q&A with a Complete solution for catching All errors in PHP; 我编写了一个Wiki风格的问答,其中包含用于捕获PHP中所有错误的完整解决方案。 which can be viewed/gleaned/stolen/critiqued here . 可以在此处查看/收集/窃取/批评。

The solution includes 5 methods that wrap all errors PHP can generate, that will eventually pass said errors up to an 'ErrorHandler' typed object. 该解决方案包括5种包装PHP可以生成的所有错误的方法,这些错误最终会将这些错误传递给“ ErrorHandler”类型的对象。

Hope some folks get some use out of it. 希望一些人从中受益。 Even if you don't steal it outright, I'm sure that the solution is at least a good example of how to handle errors in PHP - in every respect. 即使您不直接盗用它,我也确信该解决方案至少是一个如何处理PHP错误的好例子-在所有方面。

@Lucas Batistussi got points for creativity - I figure I could share my solution as well and shoot for some similar points... @Lucas Batistussi获得了创造力积分-我认为我也可以分享我的解决方案并拍摄一些类似的积分...

As of PHP 7.4.13 my experience is that all possible errors and exceptions in a program can be caught with only two callback functions:从 PHP 7.4.13 开始,我的经验是程序中所有可能的错误和异常都可以通过两个回调函数来捕获:

set_error_handler("ErrorCB");
set_exception_handler("ExceptCB");

ErrorCB simply reports its arguments in any way desired and calls Exit(). ErrorCB 只是以任何所需的方式报告其参数并调用 Exit()。

ExceptCB calls "get" methods on its exception argument and does some logic to determine where the file, line, and function are (ask me if you would like details), and reports the information in any way desired and returns. exceptCB 对其异常参数调用“get”方法,并执行一些逻辑来确定文件、行和函数的位置(询问我是否需要详细信息),并以任何所需的方式报告信息并返回。

The only need for try/catch is if you need to suppress errors for certain code, when @ or isset() isn't enough. try/catch 的唯一需要是如果您需要抑制某些代码的错误,当 @ 或 isset() 不够用时。 Using try/catch for a "main function" without setting handlers fails, since it doesn't catch all errors.在没有设置处理程序的情况下将 try/catch 用于“主函数”失败,因为它不会捕获所有错误。

If anyone finds code that generates an error that this approach doesn't catch, please let me know and I'll edit this answer.如果有人发现生成此方法无法捕获的错误的代码,请告诉我,我将编辑此答案。 One error that this approach can't intercept is a single { character near the end of a PHP program;这种方法无法拦截的一个错误是 PHP 程序末尾附近的单个 { 字符; this generates a Parse error, which requires that you run your main PHP program via an Include file that contains the error handling.这会生成解析错误,这要求您通过包含错误处理的 Include 文件运行主 PHP 程序。

I haven't found any need for register_shutdown_function().我还没有发现对 register_shutdown_function() 的任何需求。

Note that all I care about is reporting errors and then quitting the program;请注意,我只关心报告错误然后退出程序; I don't need to recover from errors--that would be a much more difficult question indeed.我不需要从错误中恢复——这确实是一个更困难的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM