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


72

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

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

欢迎使用代码示例。

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

@fopen($file);

然后再检查...但是您可以通过执行以下操作来消除@

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

或类似。

我想问题是-是否有@HAS可以用来阻止错误,并且不能以其他任何方式处理?


6
您的示例无效。“找不到文件”不是fopen()失败的唯一方法。也许文件不可读。也许它是由另一个过程打开的。错误条件取决于平台,因此您可能不想花时间思考故障案例。
杰森·科恩


4
以及为什么hack这个问题关闭了?
Akshaydeep Giri

Answers:


26

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

但是我不会仅仅抑制错误以使它们消失。这些更好地可见。


9
这样做的问题是,您最终会压制出您未曾预料到的其他错误,然后花费一整天的时间来查找未引发任何错误的错误。在TOCTOU问题的罕见情况下,我认为抛出错误要好得多,因为无论如何都不应该向最终用户显示PHP错误,但是它仍然可以使人们通过记录错误或显示错误来了解情况。如果脚本在开发环境中运行。错误抑制是隐藏问题的最佳方法。(例如,文件被删除:))
Gerry

8
很好,但是出于不被追捕和谋杀的热爱,请检查正确的错误。我曾经花太长时间追踪数据库问题-我看到Close()失败,但没有任何反应。最终,我发现巧妙的@代表了初始连接,而“ else”检查基本上是空的。删除@,我立即可以辨别出连接凭据是错误的。
匿名co

121

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

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

简短的回答:
不!

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

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

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

我花了很多年的时间才意识到由于被抑制的错误,我每两个月浪费了多少时间。多数情况下(但非排他性地),这是在安装了第三方脚本/应用程序/库之后,该脚本在开发人员环境中没有错误,但是由于php或服务器配置的差异或缺少依赖关系(通常会立即输出错误)而无法获取提醒问题出在哪里,但当开发人员添加魔术@时则不会。

备选方案(取决于情况和预期结果):
处理您所知道的实际错误,因此,如果一段代码要引起某个错误,则该代码不会在特定情况下运行。但是我认为您已经掌握了这一部分,您只是担心最终用户会看到错误,这就是我现在要解决的问题。

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

对于display_errors在php.ini中将致命错误设置为关闭(错误处理程序仍被触发)的情况,并启用错误日志记录。如果您同时具有开发服务器和实时服务器(建议使用),则在开发服务器上无需执行此步骤,因此您仍然可以调试这些致命错误,而不必诉诸于错误日志文件。使用关闭功能甚至还可以技巧性地向您的错误处理程序发送大量致命错误。

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

如果需要更多信息,可以在PHP手册的“错误控制运算符”页面上阅读我的评论


22
这是绝对正确的。抑制错误是一个基本错误。使用您的错误处理程序或使用异常,不要隐藏错误。
MattBelanger

1
即使“ isset”比“ @”快,我仍然更喜欢使用“ @”:<code> $ _LOG_TYPES = array('request','click'); $ t1 = time()。substr(microtime(),2,6); 对于($ i = 0; $ i <10000; ++ $ i){$ log_type = in_array(@ $ _ GET ['log_type'],$ _LOG_TYPES)?$ _ GET ['log_type']:'未知'; } $ t2 = time()。substr(microtime(),2,6); 回声'time_length:'。((float)$ t2-(float)$ t1); $ t1 = time()。substr(microtime(),2,6); 对于($ i = 0; $ i <10000; ++ $ i){$ log_type = in_array(isset($ _ GET ['log_type'])?$ _ GET ['log_type']:null,$ _LOG_TYPES)?$ log_type :'未知'; } $ t2 = time()。substr(microtime(),2,6); 回声'time_length:'。((float)$ t2-(float)$ t1); </ code>
diyism 2011年

13
-1模仿一个愚蠢的“是邪恶的”模因。避开内置的语言功能,而对实际用例无视,这是对货物崇拜编程的主要定义。-特别是这位大佬没有提到抑制的错误实际上并没有消失。自定义错误处理程序仍然可以使它们恢复活力(set_error_handler("var_dump");这与scream扩展的懒惰等效)。而且,这种过分的咨询导致了普通的isset()通知抑制语法变通方法,该变通方法实际上会阻碍调试(因为调试通知将被不可恢复地抑制)。
mario

4
@mario“明显忽略了实际用例”……嗯……也许您没有阅读“是邪恶的”全文。“可能有一个很好的理由,但是我还没有看到它,所以直到那一天,我认为(@)错误抑制运算符是邪恶的”。我是说我还没有找到这样的背景。看来您对Op提出的问题有一个答案,也许您可​​以考虑提交一个有更多空间讨论您的观点的解决方案。
格里

3
您的代码不会在检查索引值之前检查索引是否存在。这将导致难以跟踪问题,例如表单元素是否被重命名或输入错误。不存在的索引意味着与空白的表单元素有所不同。同样,您不应该向最终用户显示错误。如果您在开发中遇到HTML / JSON问题,并且认为可以解决这些问题,这就是我们不同意的地方。典型执行路径上的通知通常表示无法处理您应处理的条件。
格里

20

是的,压制是有道理的。

例如,如果无法打开文件,该fopen()命令将返回FALSE。很好,但是它还会产生一条PHP警告消息。通常您不想要警告-您会检查FALSE自己。

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


3
但是,可以肯定的是,可以通过首先检查file_exists($ file)来避免这种情况?
Mez

14
不,它不能,还有其他失败情况,例如“无权限读取”或“文件繁忙”。
杰森·科恩

3
直到fopen抛出您没有预料到的错误之前,这都是很棒的。您无法检查所有已知的错误情况吗?创建一个fopen包装函数。
格里2012年

我很想给你加1,只是因为你是Jason Cohen。很好的答案/评论。
Marco Demaio 2013年

@JasonCohen secure.php.net/is_read呢?但是仍然存在比赛条件...
John M.

13

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

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

5
如果你抛出一个异常,那么你并不需要严格的else,公正do_file_stuff()
MrWhite 2014年

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();

但是我宁愿让我的压制变得非常明显,因为跟在我后面的调试精神很差。


1
这是一种意见,不是很好。您可以使用$ result = @native_func();完成相同的操作。和if($ result)没有丑陋的烂摊子。我同意@是不好的,但前提是它没有得到处理。
Mfoo

您是否认为它比@fopen更洁净?您还可以禁用错误报告,但是具有更多的代码和更长的执行时间。IIRC try ... catch无法正常运行,因为它警告不是错误... try ... catch在这种情况下将是一个洁食解决方案。它正在隐藏NetBeans仅警告...
18C

我要在这里解决的问题向下一个开发人员强调了抑制已在进行中。为了实现我的目标,我选择牺牲代码行,微优化执行时间和假定的丑陋性。我看不到能够满足所有需求的“最佳方法”,因此这就是我选择权衡的方式。
ashnazg

6

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

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

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

然后至少您将获得有关系统中所有错误的全面报告。

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

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

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


我知道我不应该抑制这些错误,但是在不需要向最终用户实际显示该错误时,有些事情会抛出E_WARNING或E_NOTICE,并且在很多情况下,可以避免实际出现这些错误。除外,就目前而言,对于mysql_open
Mez

1
@martin meredith:这就是为什么您使用“ error_log”和“ display_errors = false”的原因
肯特·弗雷德里克

@Kent-到目前为止,此页面上的最佳答案![编辑:使第二好,因为我刚刚添加了一个:P] @Mez-正如Kent所建议的,设置一个仅向您显示错误的错误处理程序。
格里

5

大多数人不理解错误消息的含义。
别开玩笑了 大多数。

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

因此,除了阅读错误消息外,还应该阅读错误内容。它不仅具有一个“找不到文件”值。可以有上千种不同的错误:permission deniedsave mode restrictionopen_basedir restrictionetc.etc。每个人都需要采取适当的措施。但是,如果您插嘴,您将永远不会知道发生了什么!

OP将错误报告与错误处理搞混了,但有很大的不同!
错误处理是针对用户的。在这里“发生了什么”就足够了。
尽管错误报告是针对程序员的,但他们迫切需要知道发生了什么。

因此,切勿阻塞错误消息。两者都为程序员记录下来,并为用户处理


3

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


是的,你可以做使用error_reporting(E_ALL&〜E_NOTICE&〜E_WARNING) -但我不希望这样做,请参阅编辑问题
梅茨

3

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

error_reporting(0);

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


我更希望看到错误。我正在尝试解决的问题是,如果有任何方法您必须使用@或您可能会遇到错误,这是以前无法捕获的。
Mez

我从未见过使用@进行错误抑制的积极事例。它隐藏了所有将来的错误,而不仅仅是您要忽略的错误。
加里

不要关闭error_reporting,这太疯狂了!再次打开它时,请确保它能够将错误记录到一个文件中,以便以后读取。不向用户显示错误的正确方法是通过ini_set('display_errors',0);或更好,直接修改ini文件以包含该文件。
Chinoto Vokro'2

3

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

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

3

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

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

AFAIK,这是最优雅的方法。


首先,永远不要使用eval()。其次,如果$ script包含函数,则将对它们进行评估,然后第二次运行该函数,它将抱怨那些函数已经定义并终止。
Chinoto Vokro '16

2

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
}       

1

您不想抑制所有操作,因为它会使脚本运行缓慢。

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

<?php
    error_reporting(0);
?>

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


我不是在寻找一种关闭它的方法,我是在寻找是否有使用它的理由,因为在任何不使用@的情况下,它都无法处理(到目前为止,只有一项-mysql_connect)
Mez

@Industrial它正在做额外的工作来消除错误。由于它应该显示一个错误,但是在那里找到一个@并已动态处理它。
奥拉维尔Waage

啊! 因此,最好遵循您的示例-error_reporting(0);?
工业

1

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

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

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

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

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

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

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

在我看来,我不认为这是不良类设计的情况。错误本身是由PDOException触发的(时间戳问题从MySQL 5.6移至5.7),PHP会将其默认转储为错误日志。

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


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
}

-1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.