74

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

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

欢迎使用代码示例。

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

@fopen($file);

然后检查......但你可以通过做摆脱@

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

或类似的。

我想问题是 - 是否有任何地方可以使用 @ 来抑制错误,而不能以任何其他方式处理?

4

19 回答 19

125

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

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

简短的回答:
不!

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

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

问题是您正在抑制错误的那段代码目前可能只会导致您看到的错误;但是,当您更改被抑制的行所依赖的代码或它运行的环境时,该行很有可能会尝试输出与您试图忽略的错误完全不同的错误。那么你如何追踪一个没有输出的错误呢?欢迎来到调试地狱!

我花了很多年才意识到由于被压制的错误,我每几个月浪费了多少时间。大多数情况下(但并非唯一)这是在安装第三方脚本/应用程序/库之后,该脚本/应用程序/库在开发人员环境中没有错误,但不是我的,因为 php 或服务器配置差异或缺少通常会立即输出错误的依赖项提醒问题是什么,但不是当开发人员添加魔术@时。

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

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

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

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

如果您想了解更多信息,可以阅读我在 PHP 手册中的错误控制运算符页面上的评论。

于 2009-06-06T18:47:48.237 回答
26

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

但我不会仅仅压制错误以使它们消失。这些最好是可见的。

于 2008-09-26T07:11:09.597 回答
22

是的,压制是有道理的。

例如,如果文件无法打开,则该fopen()命令返回。FALSE这很好,但它也会产生 PHP 警告消息。通常你不想要警告——你会FALSE自己检查。

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

于 2008-09-25T23:39:33.940 回答
13

如果您不想在使用 fopen() 之类的函数时引发警告,则可以抑制错误但使用异常:

try {
    if (($fp = @fopen($filename, "r")) == false) {
        throw new Exception;
    } else {
        do_file_stuff();
    }
} catch (Exception $e) {
    handle_exception();
}
于 2008-09-26T04:07:41.563 回答
7

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

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

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

然后,至少您将获得系统中所有问题的综合报告。

如果您确实必须处理错误,则可以创建自定义错误处理程序

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

然后,您可能会发送异常(可以处理)并做任何需要向管理人员报告奇怪错误的事情。

于 2008-09-25T23:51:46.813 回答
7

我从不允许自己使用'@'...句号。

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

如果我希望我自己的代码在原生 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();

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

于 2009-07-15T20:51:08.417 回答
5

大多数人不理解错误信息的含义。
不开玩笑。他们中的大多数。

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

因此,人们应该阅读它所说的内容,而不是扼杀错误信息。它不仅有一个“找不到文件”值。可能有数千个不同的错误:permission denied, save mode restriction,open_basedir restriction等等。每一项都需要采取适当的行动。但如果你堵嘴,你永远不会知道发生了什么!

OP 将错误报告与错误处理搞乱了,但差别很大!
错误处理是针对用户的。“发生了什么事”在这里就足够了。
虽然错误报告适用于程序员,但他们迫切需要知道肯定发生了什么。

因此,永远不要堵嘴错误消息。既为程序员记录它,又为用户处理它

于 2010-10-06T08:12:34.747 回答
3

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

于 2008-09-25T23:45:49.353 回答
3

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

error_reporting(0);

在生产现场。这样,当您在开发中时,您只需注释掉该行并保持错误可见以进行调试。

于 2008-09-26T00:03:14.273 回答
3

我使用它的一个地方是在套接字代码中,例如,如果你设置了一个超时,如果你不包含@,你会收到一个警告,即使不获取数据包是有效的。

$data_len = @socket_recvfrom( $sock, $buffer, 512, 0, $remote_host, $remote_port )
于 2008-09-26T01:22:50.603 回答
3

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

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

AFAIK,这是最优雅的方式。

于 2009-07-21T00:12:16.617 回答
3

PHP 中的一些函数会发出一个E_NOTICE(例如反序列化函数)。

捕获该错误的一种可能方法(对于PHP 版本 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
}       
于 2020-03-27T13:04:32.147 回答
2

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

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

这里有更多关于这个问题的信息。

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

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

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

当我注意到跟踪输出时,我学到的教训是:有时暂时抑制错误消息是避免进一步伤害的有用的权宜之计。

在我看来,我不认为这是一个糟糕的类设计案例。错误本身是由 PDOException(从 MySQL 5.6 到 5.7 的时间戳问题)触发的,PHP 默认将所有内容都转储到错误日志中。

一般来说,由于其他评论中解释的所有原因,我不使用 @ 运算符,但在这种情况下,错误日志说服我快速做一些事情,直到问题得到正确解决。

于 2019-02-13T06:25:23.810 回答
1

您不想压制所有内容,因为它会减慢您的脚本速度。

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

<?php
    error_reporting(0);
?>

你可以阅读这个关于关闭它的 php.ini 版本。

于 2008-09-25T23:48:30.720 回答
1

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

我有两个系统,一个运行 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)的潜在错误,所以唯一的方法是抑制警告并检查它是否有效。至少,在这种情况下。

于 2021-05-14T21:17:07.887 回答
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
}
于 2010-02-16T11:18:47.060 回答
0

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

$totalCars = [];

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

@$totalCars['toyota']++;
// [
//    "toyota" => 2,
// ]
于 2021-07-05T22:57:06.857 回答
0

根据我的经验,我会说一般来说,错误抑制对于未来的开发人员来说只是另一种不好的做法,应该尽可能避免,因为它隐藏了错误的复杂性并防止错误记录,而 Exception 可以帮助开发人员获得错误快照。但是回答说“如果是这样,你会在什么情况下使用它? ”的原始问题。

我会说应该将它用于一些不会引发异常错误的遗留代码或库,而是通过将错误变量与其对象(说到 OOP)或使用全局变量来记录错误或仅打印错误来处理错误错误全部一起。

以 mysqli 对象为例

new mysqli($this->host, $this->username, $this->password, $this->db);

上面的这段代码几乎不会或从不会在连接失败时抛出异常,它只将错误存储在mysqli::errnomysli::error

对于现代编码,我发现的一种解决方案是抑制丑陋的错误消息(这对任何人都没有帮助,尤其是在调试模式关闭的生产服务器上),而开发人员应该抛出自己的异常。这是考虑现代实践并帮助编码人员更快地跟踪错误。

  $this->connection = @new mysqli($this->host, $this->username, $this->password, $this->db);
  if($this->connection->connect_errno)
      throw new mysqli_sql_exception($this->connection->error);

您可以注意到使用抑制@符号来防止在开发服务器上打开错误显示时出现难看的错误显示。

我也不得不抛出我自己的异常。这样我就可以使用@符号,同时我没有隐藏错误,也没有自己猜测错误可能是什么。

我会说,如果使用得当,那是有道理的。

于 2021-12-02T09:55:32.607 回答
-1

我在尝试加载 HTML 文件以作为 DOMDocument 对象进行处理时使用它。如果 HTML 中有任何问题...而哪个网站至少没有一个... DOMDocument->loadHTMLFile() 如果您不使用 @ 禁止它,则会抛出错误。这是我在 PHP 中成功创建 HTML 抓取工具的唯一方法(也许有更好的方法)。

于 2008-09-25T23:52:48.993 回答