【问题标题】:Determining the difference between error_reporting(0) and pre-pending @ in PHP在 PHP 中确定 error_reporting(0) 和前置 @ 之间的区别
【发布时间】:2018-04-12 22:22:59
【问题描述】:

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

目前的问题是,对于这两种情况,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

【问题讨论】:

  • 我理解它的用途和目的,但是我想把这个讨论排除在这个问题的范围之外。 dkellner's comment 总结得很好 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.

标签: php error-handling error-reporting


【解决方案1】:

除了error_reporting(),还可以用ini_get('error_reporting')查看报错级别。从理论上讲,这是相似的,但它有一个细微的差别,这使得它对你想要做的事情很有价值。

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

因此您可以比较这两个值并确定到底发生了什么:

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

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

如果您遵循set_error_handler 的 PHP 文档中的示例,则自定义错误处理程序的函数签名可能如下所示:

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)上对我有用。

【讨论】:

  • 我在发布问题之前尝试了类似的方法。复制了您的确切代码后,这不适用于 CentOS 6.8 上的 PHP 5.3.3 - 使用代码 sn-p 更新了主要问题
  • 嗯,有趣的是它不适用于较旧的 PHP 版本...您的应用程序是否有任何类型的初始化阶段总是首先运行?也许您可以在脚本的最开始检查错误报告级别并将其保存在全局变量中,然后您可以稍后在错误处理函数中检查它是否已更改。我将集体讨论其他解决方案,但这是我想到的第一个替代方案。
  • 是的,这是一个有趣的问题。对于我的特定应用程序,我可以事先记录级别,但我希望可能有另一个解决方案不需要此步骤来使错误处理程序更通用。如果 ini_get() 起作用了,那就完美了!
  • 经过更多测试后,我发现使用 CentOS 6.9 上的 PHP 5.6.23 解决方案也失败了,但 Fedora 25 上的 PHP 7.0.23 可以工作
  • 我开始认为没有快速或简单的方法可以做到这一点,但我确实想到了一种潜在的解决方案。如果您真的打算这样做,请参阅我的答案的更新部分。
猜你喜欢
  • 2010-12-29
  • 2016-01-08
  • 2014-10-09
  • 2011-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-13
  • 2010-12-13
相关资源
最近更新 更多