用PHP中的@运算符抑制错误

时间:2008-09-25 23:37:42

标签: php operators error-suppression

在您看来,使用@运算符来抑制PHP中的错误/警告是否有效,而您可能正在处理错误?

如果是这样,你会在什么情况下使用它?

欢迎使用代码示例。

编辑:回复者注意。我不打算关闭错误报告,但是,例如,通常的做法是使用

@fopen($file);

然后再检查......但是你可以通过

来摆脱@
if (file_exists($file))
{
    fopen($file);
}
else
{
    die('File not found');
}

或类似的。

我想问题是 - 是否有任何@HAS用于抑制错误,不能以任何其他方式处理?

18 个答案:

答案 0 :(得分:116)

注意:首先,我意识到99%的PHP开发人员使用错误抑制操作符(我曾经是其中之一),所以我期待任何看到这个的PHP开发人员不同意。

  

在您看来,使用@运算符来抑制PHP中的错误/警告是否有效,而您可能正在处理错误?

简答:
不!

更正确的答案:
我不知道,因为我不知道一切,但到目前为止,我还没有遇到过这是一个很好的解决方案。

为什么不好:
在我认为使用PHP大约7年的时候,我已经看到了由错误抑制操作员引起的无休止的调试痛苦,并且从未遇到过无法避免的情况。

问题在于您要阻止错误的代码,目前可能只会导致您看到的错误;但是,当您更改受抑制的行所依赖的代码或其运行的环境时,该行有可能尝试从您尝试忽略的错误中输出完全不同的错误。那么如何追踪未输出的错误?欢迎调试地狱!

我花了很多年才意识到由于错误的原因,我每隔几个月就浪费了多少时间。最经常(但不是唯一)这是在安装第三方脚本/应用程序/库之后,在开发人员环境中没有错误,但不是我的,因为php或服务器配置差异或缺少依赖性,这通常会立即输出错误警告问题是什么,但不是当开发者添加魔法时。

替代方案(取决于情况和期望的结果):
处理您所知道的实际错误,以便如果一段代码会导致某个错误,那么它就不会在该特定情况下运行。但我认为你得到了这个部分而你只是担心最终用户会看到错误,这就是我现在要解决的问题。

对于常规错误,您可以设置错误处理程序,以便在您查看页面时以您希望的方式输出错误处理程序,但对最终用户隐藏并记录,以便您知道用户正在触发的错误。

对于致命错误,在php.ini中将display_errors设置为关闭(您的错误处理程序仍会被触发)并启用错误记录。如果你有一个开发服务器和一个实时服务器(我推荐),那么在开发服务器上不需要这一步,因此你仍然可以调试这些致命错误,而不必诉诸于查看错误日志文件。甚至还有一个trick using the shutdown function会向您的错误处理程序发送大量致命错误。

总结:
请避免它。可能有一个很好的理由,但我还没有看到一个,所以直到那天我认为(@)错误抑制操作符是邪恶的。

如果您想了解更多信息,可以在PHP手册中阅读my comment on the Error Control Operators page

答案 1 :(得分:24)

我会压制错误并处理它。否则,您可能会遇到 TOCTOU 问题(检查时间,使用时间。例如,在file_exists返回true之后,但在fopen之前,文件可能会被删除)。

但我不会压制错误让它们消失。这些更好看。

答案 2 :(得分:18)

是抑制是有道理的。

例如,如果无法打开文件,fopen()命令将返回FALSE。这没关系,但它会产生一条PHP警告信息。通常你不需要警告 - 你自己会检查FALSE

事实上,PHP manual特别建议在这种情况下使用@!

答案 3 :(得分:12)

如果您不希望在使用fopen()等函数时抛出警告,则可以抑制错误但使用异常:

try {
    if (($fp = @fopen($filename, "r")) == false) {
        throw new Exception;
    } else {
        do_file_stuff();
    }
} catch (Exception $e) {
    handle_exception();
}

答案 4 :(得分:7)

我从不允许自己使用'@'...期间。

当我在代码中发现'@'的使用时,我添加了注释,使其在使用时和使用它的函数周围的docblock中都非常明显。由于这种错误抑制,我也被“追逐鬼”调试所困扰,我希望通过在找到它时突出显示它的用法,使下一个人更容易。

如果我希望自己的代码在本机PHP函数遇到错误时抛出异常,并且'@'似乎是最简单的方法,我会选择做其他相同的事情结果,但是(再次)在代码中显而易见:

$orig = error_reporting(); // capture original error level
error_reporting(0);        // suppress all errors
$result = native_func();   // native_func() is expected to return FALSE when it errors
error_reporting($orig);    // restore error reporting to its original level
if (false === $result) { throw new Exception('native_func() failed'); }

这只是编写的代码:

$result = @native_func();

但我更喜欢让我的抑制需要非常明显,为了跟随我的糟糕的调试灵魂。

答案 5 :(得分:6)

应该避免错误抑制,除非你知道你可以处理所有条件。

这可能比最初看起来要困难得多。

你真正应该做的是依靠php的“error_log”作为你的报告方法,因为你不能依赖用户查看页面来报告错误。 (你还应该禁止php显示这些错误)

那么至少你会得到一份关于系统中出现问题的综合报告。

如果您真的必须处理错误,可以创建自定义错误处理程序

http://php.net/set-error-handler

然后你可以发送异常(可以处理)并做一些事情来报告管理中的奇怪错误。

答案 6 :(得分:5)

大多数人都不理解错误信息的含义 不开玩笑。其中大部分都是。

他们认为错误信息都是一样的,说“出了问题!” 他们懒得读它 虽然它是错误信息中最重要的部分 - 不仅仅是它已被提出的事实,而是它的含义。它可以告诉你 出了什么问题。错误消息是为了帮助,而不是打扰你“如何隐藏它?”问题。这是新手网络编程世界中最大的误解之一。

因此,不应该呕吐错误消息,而应该读取它所说的内容。它不仅有一个“找不到文件”的值。可能存在数千种不同的错误:permission deniedsave mode restrictionopen_basedir restriction等等。每个人都需要采取适当行动但如果你唠叨它,你永远不会知道发生了什么!

OP正在搞错误报告错误处理,但这是非常大的区别!
错误处理是针对用户的。 “发生的事情”就足够了。
虽然错误报告是针对程序员的,但他们迫切需要知道发生了什么。

因此,永远不要唠叨错误消息。程序员记录,用户处理

答案 7 :(得分:3)

有没有办法压制php.ini警告和错误?在这种情况下,您只能调试更改标志而不是尝试发现哪个@隐藏了问题。

答案 8 :(得分:3)

我真正需要使用它的唯一地方是eval功能。 eval的问题在于,当由于语法错误而无法解析字符串时,eval不返回false,而是抛出错误,就像在常规脚本中出现解析错误一样。为了检查存储在字符串中的脚本是否可解析,您可以使用以下内容:

$script_ok = @eval('return true; '.$script);

AFAIK,这是最优雅的方式。

答案 9 :(得分:3)

使用@有时会适得其反。根据我的经验,您应该始终在php.ini中关闭错误报告或调用

error_reporting(0);

在生产网站上。这样,当您处于开发阶段时,您可以注释掉该行并保持错误以便进行调试。

答案 10 :(得分:2)

我使用它的一个地方是套接字代码,例如,如果你有一个超时设置,如果你不包含@,你就会得到一个警告,即使它没有得到一个数据包也是有效的。

$data_len = @socket_recvfrom( $sock, $buffer, 512, 0, $remote_host, $remote_port )

答案 11 :(得分:2)

PHP中的某些函数将发出E_NOTICE(例如unserialize函数)。

(对于PHP versions 7+,捕获该错误的一种可能方法是将所有已发布的错误转换为异常,而不是让其发布E_NOTICE。我们可以如下更改异常错误处理程序:

function exception_error_handler($severity, $message, $file, $line) {                                                                       
    throw new ErrorException($message, 0, $severity, $file, $line);          
}                                                                            

set_error_handler('exception_error_handler');                          

try {             
    unserialize('foo');
} catch(\Exception $e) {
    // ... will throw the exception here
}       

答案 12 :(得分:1)

你不想压制所有内容,因为它会降低你的脚本速度。

是的,在php.ini和脚本中都有一种方法可以删除错误(但只有当你在实时环境中并从php中记录错误时才这样做)

<?php
    error_reporting(0);
?>

你可以阅读this获取关闭它的php.ini版本。

答案 13 :(得分:0)

如果您正在使用自定义错误处理功能并想要抑制错误(可能是已知错误),请使用此方法。在这种情况下使用'@'不是一个好主意,因为如果设置了错误处理程序,它不会抑制错误。

写3个函数并像这样调用。

# supress error for this statement
supress_error_start();  
$mail_sent = mail($EmailTo, $Subject, $message,$headers);
supress_error_end(); #Don't forgot to call this to restore error.  

function supress_error_start(){
    set_error_handler('nothing');
    error_reporting(0);
}

function supress_error_end(){
    set_error_handler('my_err_handler');
    error_reporting('Set this to a value of your choice');
}

function nothing(){ #Empty function
}

function my_err_handler('arguments will come here'){
      //Your own error handling routines will come here
}

答案 14 :(得分:0)

今天我遇到了一个问题,这是一个很好的例子,说明何时可能要至少临时使用@运算符。

长话短说,我发现登录信息(纯文本格式的用户名和密码)写在错误日志跟踪中。

有关此问题的更多信息。

登录逻辑属于其自己的一类,因为系统应该提供不同的登录机制。由于服务器迁移问题,发生了错误。该错误将整个跟踪转储到错误日志中,包括密码信息!一种方法以用户名和密码作为参数,因此trace将所有内容忠实地写入错误日志。

这里的长期解决方法是重构所述类,而不是将用户名和密码用作2个参数,例如,使用包含这2个值的单个数组参数(在这种情况下,trace将为参数写出Array)。还有其他解决此问题的方法,但这是一个完全不同的问题。

无论如何。跟踪消息很有用,但在这种情况下是完全有害的。

当我注意到跟踪输出时,我吸取了教训:有时暂时抑制错误消息是一种有用的停止间隙措施,以避免进一步的伤害。

在我看来,我不认为这是糟糕的班级设计案例。错误本身是由PDOException触发的(时间戳问题从MySQL 5.6移至5.7),PHP刚将其默认转储为错误日志。

通常,出于其他注释中解释的所有原因,我不使用@运算符,但是在这种情况下,错误日志使我迅速采取了措施,直到问题得到正确解决。

答案 15 :(得分:0)

我认为是使用@ 抑制错误的有效用例。

我有两个系统,一个运行 PHP 5.6.something,另一个运行 PHP 7.3.something。我想要一个可以在它们两个上正常运行的脚本,但有些东西在 PHP 5.6 中不存在,所以我使用了像 random_compat 这样的 polyfill。

最好使用内置函数,所以我的代码如下所示:

if(function_exists("random_bytes")) {
    $bytes = random_bytes(32);
} else {
    @include "random_compat/random.php"; // Suppress warnings+errors
    if(function_exists("random_bytes")) {
        $bytes = random_bytes(32);
    } else if(function_exists('openssl_random_pseudo_bytes')) {
        $bytes = openssl_random_pseudo_bytes(4);
    } else {
        // Boooo! We have to generate crappy randomness
        $bytes = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',64)),0,32);
    }
}

回退到 polyfill 不应产生任何错误或警告。在尝试加载必要的 polyfill 后,我正在检查该函数是否存在。甚至有后备的后备。以及回退到回退。

无法避免 include 的潜在错误(例如使用 file_exists),因此唯一的方法是抑制警告并检查它是否有效。至少,在这种情况下。

答案 16 :(得分:0)

我可以想到一种使用情况,用于自动递增一个不存在的数组键。

$totalCars = [];

$totalCars['toyota']++; // PHP Notice:  Undefined index: toyota

@$totalCars['toyota']++;
// [
//    "toyota" => 2,
// ]

答案 17 :(得分:-1)

我在尝试加载HTML文件以作为DOMDocument对象进行处理时使用它。如果HTML中存在任何问题...而且哪个网站没有至少一个 ... DOMDocument-&gt; loadHTMLFile()如果你没有用它来抑制它就会抛出错误@。这是唯一的方法(也许有更好的方法)我曾经成功地用PHP创建HTML scraper。