使用自定义关闭处理程序时防止PHP致命错误回溯输出

时间:2011-12-15 20:16:58

标签: php error-handling

在理解处理PHP错误时,我必须遗漏一些东西,特别是抑制它们的输出。当发生致命错误时,我希望我的关闭处理程序函数能够正常处理它并终止脚本执行。这按预期工作。 但是,我似乎无法阻止PHP输出有关致命错误的信息。

我的php.ini文件包含以下指令:

error_reporting = E_ALL | E_STRICT
display_errors = Off

我设置error_reporting报告所有内容,我使用自定义错误处理程序来抛出异常。我的期望是display_errors = Off将阻止显示任何错误消息。

无论如何,当发生致命错误时,绕过自定义错误处理程序(因为脚本执行立即停止)并执行关闭处理程序。

现在,我的简化代码:

error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'Off');

function shutdown_handler()
{
  $err = error_get_last();
  $fatal = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR);
  if ($err && in_array($err['type'], $fatal)) {
    echo "\n\ntest fatal error output\n\n";
  }
  exit();
}

register_shutdown_function('shutdown_handler');

为了测试它我生成一个“允许的内存大小耗尽”致命错误,如下所示:

// max out available memory
$data = '';
while(true) {
  $data .= str_repeat('#', PHP_INT_MAX);
}

因为我有display_errors = Off我希望这只产生以下输出(根据关机处理程序):

test fatal error output

但我继续收到:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 2147483648 bytes) in /home/daniel/mydev/php/test0.php on line 24
PHP Stack trace:
PHP   1. {main}() /home/daniel/mydev/php/test0.php:0
PHP   2. str_repeat() /home/daniel/mydev/php/test0.php:24

test fatal error output

我错过了哪些可以防止此错误跟踪输出?


结论

正如@Cthos sagely指出的那样,“E_ERROR和display_errors不能很好地融合在一起。”

这也是E_PARSE的情况(我假设E_CORE_ERROR / E_COMPILE_ERROR,但我没有破坏我的PHP安装来测试它)。我认为在这些情况下PHP会强制将错误追溯到STDOUT是有道理的,因为如果不这样做,你可能永远不会知道是否/为什么会出错。

在这种情况下,解决方案是:

  1. 正如@Cthos建议的那样,在php.ini:error_reporting = (E_ALL & ~ E_ERROR)中或在运行时使用error_reporting(E_ALL & ~ E_ERROR);沉默E_ERROR通知
  2. 更新关闭处理程序以检查上一个错误是否为E_ERROR类型,如果是,则执行相应的操作。
  3. 至于其他致命因素,如E_PARSE,E_CORE_ERROR等,你只需要确保你的代码是正确的,你的PHP是有效的。如果你试图压制E_PARSE错误并在关机函数中处理它们,它将无法工作,因为解析错误会阻止PHP走得那么远。

    因此,更新/工作的关闭处理程序如下所示:

    error_reporting(E_ALL & ~ E_ERROR);
    
    function shutdown_handler()
    {
      $err = error_get_last();
      if ($err && $err['type'] == E_ERROR) {
        $msg = 'PHP Fatal Error: '.$err['message'].' in '.$err['file'].
          ' on line '.$err['line'];
        echo $msg, PHP_EOL;
      }
      exit();
    }
    

2 个答案:

答案 0 :(得分:3)

一些可能的解决方案,回答是因为评论部分对我生气。

使用解决方案编辑#3:

显然E_ERROR和display_errors不能很好地协同发挥

您可以将error_reporting设置为E_ALL& ~E_ERROR,让你的关闭处理程序处理致命的事情(因为无论如何这是最后一件事应该被调用)。

此外,E_ALL在PHP 5.4.0之前不包含E_DEPRECATED,因此如果您想要捕获它,请使用~0 & ~E_ERROR


display_errors可能会被窃听

这里有一个关于如何让它吐出错误的要点,即使你告诉它不要: https://gist.github.com/1483028

如果您通过ini_set()将display_errors设置为0,它仍会显示致命错误

  

尽管可以在运行时设置display_errors(使用ini_set()),但如果脚本存在致命错误,则不会产生任何影响。这是因为未执行所需的运行时操作。

此外,你也可以将它发送给stderr,这太棒了。

http://www.php.net/manual/en/errorfunc.configuration.php#ini.display-errors

编辑2: 确保您已更改正确的php.ini

想到这一点,并评论如此,但有不止一个php.ini文件。如果您在命令行中执行此操作,则需要编辑cli one(Ubuntu上的/etc/php5/cli/php.ini),而不是web编辑(/ etc / php5 / apache2 / php)。 INI)

我猜你只需要将php.ini设置为display_errors为0。

答案 1 :(得分:3)

不需要的错误信息是由错误日志记录配置(log_errorserror_log)引起的,并且完全独立于display_errors配置。从命令行运行脚本时,默认的php.ini配置会将错误详细信息记录到stderr。在Apache下,它们显示在/etc/httpd/logs/error_log

查看PHP源代码main.c中的php_error_cb函数。特别是,第1056行清楚地表明格式为“PHP%s:%s在%s上的%s”格式的消息是由错误日志记录配置引起的(由display_errors引起的错误信息将不具有“PHP:”前缀)。