【问题标题】:Catch PHP Fatal Error捕获 PHP 致命错误
【发布时间】:2011-01-20 20:31:16
【问题描述】:

我有一个启用了 RESTful 的 Web 服务站点,因此其他网站/ajax 脚本可以调用该站点来获取/设置数据。但是,每当 Web 服务站点以某种方式返回 PHP 致命错误时,返回的 HTTP 状态都是 200 而不是 500。有没有办法修复它,以便在发生致命错误时返回 500 而不是 200?或者,如果不可能,如何更改我的客户端以识别 Web 服务返回的致命错误?

【问题讨论】:

标签: php http-status-codes


【解决方案1】:

PHP 确实在发生致命错误时发送 HTTP 500。
让我引用 this PHP bug-report 中的评论(一个 xdebug 错误阻止了这种行为):

确实会发生。它要求:

1) display_errors = 关闭
2) 错误发生时尚未发送任何标头
3)当时状态是200。

<?
    ini_set('display_errors', 0); 
    foobar();
    // This will return a http 500
?>

您可以使用shutdown functionerror_get_last 捕获致命错误:

register_shutdown_function(function() {
    $lastError = error_get_last();

    if (!empty($lastError) && $lastError['type'] == E_ERROR) {
        header('Status: 500 Internal Server Error');
        header('HTTP/1.0 500 Internal Server Error');
    }
});

【讨论】:

  • 如果你启用了 xdebug,我不会认为它会用它自己的错误处理程序覆盖它,这似乎也返回一个 HTTP 200。
【解决方案2】:

一种可能的方法是将默认响应设置为 500,如果一切执行成功,则将响应设置为 200:

http_response_code(500);
render_my_page();
http_response_code(200);

【讨论】:

  • 放入一个大的页面缓冲区以防止关于输出已经开始的臭名昭著的错误,应该这样做。
  • 那个,或者确保将 &lt;?php header('500 Internal Server Error'); ?&gt; 放在脚本的开头而不是在棘手部分之前。
【解决方案3】:

创建自定义错误处理程序 (set_error_handler) 并调用 header("HTTP/1.0 500 Service not available");

编辑:

根据我回答的第一条评论,您无法捕获真正的致命错误。但是,如果禁用输出缓冲并且错误不显示在屏幕上,PHP 将默认设置 500 错误代码来处理致命错误。

<?php
        $x = y();
?>

如果没有向屏幕发送任何内容,上述代码将返回 500 错误代码。

因此,如果您希望这种错误设置正确的代码,请自己进行缓冲:

<?php
        $buffer = 'blah';
        $x = y();  // will trigger 500 error
        echo $buffer;
?>

【讨论】:

  • 自定义错误处理程序无法捕获致命错误(例如,调用非对象上的方法;时间限制/内存限制已用完)或解析错误。来自文档:The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNINGphp.net/set_error_handler
  • 是的,这值得一提。我并没有假设他的字面意思是致命的,因为有些人用它来表示任何类型的无法恢复的错误。
  • 那么您需要禁用所有输出缓冲并避免显示任何数据,直到脚本结束(如我编辑的答案所示)或默认为 500 代码并在成功时调整为 200(正如其他人所建议的那样)。
  • 您可以使用 register_shutdown_function 捕获它们,但您将在执行结束时。
【解决方案4】:
function fatal_error_handler() {

  if (@is_array($e = @error_get_last())) {
    if (isset($e['type']) && $e['type']==1) {
      header("Status: 500 Internal Server Error");
      header("HTTP/1.0 500 Internal Server Error");
      }
    }

}
register_shutdown_function('fatal_error_handler');

【讨论】:

    【解决方案5】:

    我开发了一种方法来捕获 PHP 中的所有错误类型(几乎所有)!我不确定 E_CORE_ERROR (我认为它不适用于该错误)!但是,对于其他致命错误(E_ERROR、E_PARSE、E_COMPILE...),仅使用一个错误处理函数就可以正常工作!这是我的解决方案:

    将以下代码放在您的主文件(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();
    
    ?>
    

    我希望这可以帮助很多人!

    【讨论】:

    • 这是我第一次看到“状态:”HTTP 标头。
    【解决方案6】:

    我认为您希望在部署应用程序之前捕获并修复所有致命错误,因为其中许多是代码错误、缺少包含、不存在的对象,这些都是开发错误。即使是内存不足的错误也可以通过节省内存的编码技术来最小化(最大的胜利之一是使用无缓冲的查询并处理数据并在返回结果集时发出输出,而不是抛出巨大的数组)。

    【讨论】:

    • 是的,我应该,但是在开发过程中有点烦人
    • 有时致命错误超出您的控制范围。 APC 有时会处于不良状态,并在您重新启动 apache 之前抛出错误。然后,神奇地,无需更改任何代码,致命错误就会消失。
    • 这个答案本质上是说,“只需编写完美的代码并确保它是完美的,并且您的代码所依赖的任何东西也是完美的。”好的,我会马上解决的。
    猜你喜欢
    • 1970-01-01
    • 2022-12-10
    • 1970-01-01
    • 2019-04-20
    • 2021-12-13
    • 2014-03-31
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多