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

使用自定义关机处理程序时防止PHP致命错误回溯输出,php,error-handling,Php,Error Handling,我在处理PHP错误的理解中肯定遗漏了一些东西,特别是抑制它们的输出。当发生致命错误时,我希望我的shutdown handler函数能够优雅地处理它并终止脚本执行。这正如预期的那样有效然而,我似乎无法阻止PHP输出有关致命错误的信息。 我的php.ini文件包含以下指令: error_reporting = E_ALL | E_STRICT display_errors = Off 我将error\u reporting设置为报告所有内容,并使用自定义错误处理程序抛出异常。我的期望是displ

我在处理PHP错误的理解中肯定遗漏了一些东西,特别是抑制它们的输出。当发生致命错误时,我希望我的shutdown handler函数能够优雅地处理它并终止脚本执行。这正如预期的那样有效然而,我似乎无法阻止PHP输出有关致命错误的信息。

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

error_reporting = E_ALL | E_STRICT
display_errors = Off
我将
error\u reporting
设置为报告所有内容,并使用自定义错误处理程序抛出异常。我的期望是
display\u 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\u errors=Off
我希望这只会产生以下输出(根据关机处理程序):

但是,我继续收到:

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明智地指出的,“E_错误和display_错误不能很好地结合在一起。”

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

因此,这种情况下的解决方案是:

  • 正如@Cthos所建议的,在php.ini中关闭E_错误通知:
    ERROR_reporting=(E_ALL&~E_ERROR)
    或在运行时使用
    ERROR_reporting(E_ALL&~E_ERROR)
  • 更新关机处理程序以检查上一个错误是否属于E_错误类型,如果是,则执行相应的操作
  • 至于其他致命错误,比如E_PARSE、E_CORE_ERROR等,你只需要确保你的代码是正确的,并且你的PHP工作正常。如果您试图使E_解析错误保持沉默,并在关闭函数中处理它们,那么它将不起作用,因为解析错误会阻止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();
    }
    

    两个可能的解决方案,回答是因为评论部分对我很生气

    使用解决方案编辑#3:

    显然,E_错误和显示错误不能很好地配合使用

    您可以将error_reporting设置为E_ALL&~E_error,然后让您的关机处理程序处理致命错误(因为这是无论如何都不应该调用的事情)

    此外,
    E_ALL
    不包含PHP5.4.0之前的
    E_DEPRECATED
    ,因此如果您也想捕捉到这一点,请使用
    ~0&~E_ERROR


    显示错误可能被窃听

    下面是一个要点,告诉您如何让它吐出错误,即使您告诉它不要这样做:

    如果通过ini\u set()将“显示错误”设置为0,则仍将显示致命错误

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

    此外,您还可以将其发送到stderr,这太棒了

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

    考虑到这一点,并进行了评论,但这里有不止一个php.ini文件。如果在命令行上执行此操作,则需要编辑cli(Ubuntu上为/etc/php5/cli/php.ini),而不是web(etc/php5/apache2/php.ini)


    我猜您只需要将php.ini设置为显示错误为0。

    不需要的错误信息是由于错误记录配置(and)造成的,并且完全独立于
    显示错误配置。从命令行运行脚本时,默认php.ini配置将错误详细信息记录到
    stderr
    。在Apache下,它们出现在


    查看PHP源代码的
    main.c
    中的函数。特别是,第1056行明确指出,任何格式为“PHP%s:%s In%s In%d In第%d行”的消息都是由于错误日志记录配置造成的(由于
    display\u errors
    导致的错误信息将不会有“PHP:”前缀)。

    您是否应该使用自定义错误处理程序@Cthos——正如问题所述,我已经使用了一个自定义错误处理程序。发生致命错误时,脚本执行会立即停止,并且不会调用自定义错误处理程序。然而,一个定制的关机处理程序仍然可以运行。Hrm,当时我在阅读理解方面失败了,但我原以为命运仍然会降临到这个处理程序身上。实验时间。@Cthos在这方面不用担心,我是最差的。因为我的a.D.D.:)我肯定会不时投反对票。。。是的,我已经在命令行上试验了半个小时,试图让它工作。这对我来说很有效,所以也许是你的错误报告级别?是的--这也是我的想法,但我仍然得到错误输出(奇怪),这就是为什么我最终发布了这个问题。。。可能是虫子吗?我想……可能吧。您正在编辑哪个php.ini?如果您使用的是ubuntu,则有2个:/etc/php5/cli/php.ini和/etc/php5/apache2/php.ini。如果编辑了其中一个,但未编辑另一个。。(但你可能知道这一点,只是想了解一下)。遗憾的是,我使用的是正确的(也是我fedora发行版中唯一的一个)php.ini…@rdlowrey-什么版本的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();
    }