确定 PHP 中 error_reporting(0) 和 pre-pending @ 之间的区别

Determining the difference between error_reporting(0) and pre-pending @ in PHP

提问人:Liam Freke 提问时间:11/1/2017 最后编辑:Liam Freke 更新时间:11/1/2017 访问量:121

问:

在为 PHP 系统编写了一个自定义错误处理程序后,我试图弄清楚如何确定错误报告级别(可以通过 error_reporting()) 获取)是否已全局设置为 Off (0) 或仅使用 @ 前缀禁用该行

目前的问题是,对于这两种情况,error_reporting() 函数都返回 0

更新 #1

不幸的是,这个例子不适用于 CentOS 6.8 上的 PHP 5.3.3,因为 ini_get() 函数返回与 error_reporting() 相同的值

function errhandle($errno, $errstr, $errfile, $errline) {
    if (error_reporting() === 0 && error_reporting() === (int)ini_get('error_reporting')) {
        echo 'error_reporting(0) was used';
    } else if (error_reporting() === 0 && error_reporting() !== (int)ini_get('error_reporting')) {
        echo '@ was used';
    }
}

set_error_handler('errhandle');

echo error_reporting()."\n";
echo @$arr['name'];

// Prints
22527
error_reporting(0) was used
PHP 处理 错误 报告

评论

0赞 Liam Freke 11/1/2017
我理解它的用途和目的,但我想将该讨论排除在这个问题的范围之外。德凯尔纳的评论很好地总结了这一点There is no reason to NOT use something just because "it can be misused". You could as well say "unlink is evil, you can delete files with it so don't ever use unlink". It's a valid point that the @ operator hides all errors - so my rule of thumb is: use it only if you're aware of all possible errors your expression can throw AND you consider all of them irrelevant.

答:

2赞 Ian Drake 11/1/2017 #1

除了 之外,还可以使用 检查错误报告级别。从理论上讲,这是相似的,但它有一个微妙的区别,使它对你正在尝试做的事情很有价值。error_reporting()ini_get('error_reporting')

如果错误报告已全局禁用,则将返回字符串 .但是,如果错误报告被单独保留,并且该行以 为前缀,它将返回一个非零值(现有 INI 指令的值)。error_reporting(0)ini_get('error_reporting')0@

因此,您可以比较 2 个值并准确确定发生了什么:

if (error_reporting() === 0 && error_reporting() === (int)ini_get('error_reporting')) {
    echo 'error_reporting(0) was used';
} else if (error_reporting() === 0 && error_reporting() !== (int)ini_get('error_reporting')) {
    echo '@ was used';
}

这只是您可以在自定义错误处理程序中执行的条件类型的一个示例。如果它不是你想要的,请告诉我,我会尝试调整它。

在我的 PHP 7.1.2(Windows 上的 CLI)上进行了本地测试。


更新 #1

经过一番思考,我想出了这个主意。只是一个警告,这似乎非常黑客和低效,但我开始认为这可能是唯一的方法。

如果您按照 PHP 文档中的示例进行set_error_handler,那么自定义错误处理程序的函数签名可能如下所示:

function myErrorHandler($errno, $errstr, $errfile, $errline)

换句话说,您拥有发生错误的文件和行号。您可以使用此信息打开文件本身,查看该行号,解析标记并查找字符。代码如下所示:@

function myErrorHandler($errno, $errstr, $errfile, $errline) {
    $errfileContents = file($errfile);
    $errlineContents = $errfileContents[$errline - 1];
    $tokens = token_get_all('<?php ' . $errlineContents . ' ?>');
    if (error_reporting() === 0) {
        if (in_array('@', $tokens)){
            echo '@ was used';
        } else {
            echo 'error_reporting(0) was used';
        }
    }
}

显然,您可以通过检查是否同时使用了这两种技术或两者都没有使用来扩展该条件。

无论如何,它很丑陋,但它在我的 PHP 5.6.6(Windows 上的 CLI)上对我有用。

评论

0赞 Liam Freke 11/1/2017
在发布问题之前,我尝试了类似的方法。复制了您的确切代码后,这不适用于 CentOS 6.8 上的 PHP 5.3.3 - 用代码片段更新了主要问题
0赞 Ian Drake 11/1/2017
嗯,这很有趣,它不适用于较旧的 PHP 版本......你的应用是否有任何类型的初始化阶段总是首先运行?也许您可以在脚本的最开头检查错误报告级别并将其保存在全局变量中,然后您可以稍后在错误处理程序函数中检查它,看看它是否发生了变化。我将集思广益其他解决方案,但这是我想到的第一个替代方案。
0赞 Liam Freke 11/1/2017
是的,这是一个有趣的问题。对于我的特定应用程序,我有可能事先记录级别,但我希望可能有另一种解决方案不需要此步骤来使错误处理程序更加通用。如果ini_get()起作用了,那就完美了!
0赞 Liam Freke 11/1/2017
经过更多的测试,我发现在 CentOS 6.9 上的 PHP 5.6.23 解决方案也失败了,但 Fedora 25 上的 PHP 7.0.23 可以工作
0赞 Ian Drake 11/1/2017
我开始认为没有快速或简单的方法可以做到这一点,但我确实想到了一个潜在的解决方案。如果您真的打算这样做,请参阅我答案的更新部分。