我对 @ 符号的实际作用感到困惑,在进行了一些实验后得出以下结论
* 无论错误报告设置在哪个级别,或者语句前面是否带有 @,设置的错误处理程序都会被调用。
* 由错误处理程序来赋予不同错误级别某种意义。您可以使自定义错误处理程序回显所有错误,即使错误报告设置为 NONE。
* 那么 @ 运算符的作用是什么?它会暂时将该行的错误报告级别设置为 0。如果该行触发错误,错误处理程序仍将被调用,但它将使用错误级别 0 被调用。
希望这对某些人有所帮助
PHP 支持一个错误控制运算符:at 符号(@
)。当在 PHP 中将它放在表达式前面时,该表达式可能生成的任何诊断错误都将被抑制。
如果使用 set_error_handler() 设置了自定义错误处理函数,即使诊断已被抑制,它仍然会被调用。
在 PHP 8.0.0 之前,如果错误被 @
运算符抑制,则在自定义错误处理函数内部调用的 error_reporting() 始终返回 0
。从 PHP 8.0.0 开始,它返回此(按位)表达式的值:E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE
。
表达式生成的任何错误消息都可以在 error_get_last() 返回的数组的 "message"
元素中找到。该函数的结果将在每次错误时发生变化,因此需要尽早检查。
<?php
/* 故意文件错误 */
$my_file = @file ('non_existent_file') or
die ("Failed opening file: error was '" . error_get_last()['message'] . "'");
// 这适用于任何表达式,而不仅仅是函数:
$value = @$cache[$key];
// 如果索引 $key 不存在,则不会发出通知。
?>
注意:
@
运算符仅适用于 表达式。一个简单的经验法则是:如果可以获取某事物的价值,那么就可以在它前面加上@
运算符。例如,它可以放在变量、函数调用、某些语言结构调用(例如 include)等前面。它不能放在函数或类的定义前面,也不能放在条件结构(如if
和 foreach)等前面。
在 PHP 8.0.0 之前,@
运算符可能会禁用会导致脚本执行终止的关键错误。例如,将 @
放在不可用或拼写错误的函数调用的前面,会导致脚本终止,而没有任何指示说明原因。
我对 @ 符号的实际作用感到困惑,在进行了一些实验后得出以下结论
* 无论错误报告设置在哪个级别,或者语句前面是否带有 @,设置的错误处理程序都会被调用。
* 由错误处理程序来赋予不同错误级别某种意义。您可以使自定义错误处理程序回显所有错误,即使错误报告设置为 NONE。
* 那么 @ 运算符的作用是什么?它会暂时将该行的错误报告级别设置为 0。如果该行触发错误,错误处理程序仍将被调用,但它将使用错误级别 0 被调用。
希望这对某些人有所帮助
注意在 include() 之前的语句中使用错误控制运算符,例如
<?PHP
(@include("file.php"))
OR die("Could not find file.php!");
?>
这会导致包含的文件的错误报告级别也设置为零。因此,如果包含的文件中存在某些错误,则不会显示它们。
如果您想知道使用 @ 运算符的性能影响,请考虑以下示例。在这里,第二个脚本(使用 @ 运算符)需要 1.75 倍的时间来执行……几乎是第一个脚本的两倍时间。
因此,虽然确实存在一些开销,但每次迭代,我们看到 @ 运算符每次调用只增加了 0.005 毫秒。在我看来,这不是避免使用 @ 运算符的理由。
<?php
function x() { }
for ($i = 0; $i < 1000000; $i++) { x(); }
?>
real 0m7.617s
user 0m6.788s
sys 0m0.792s
对比
<?php
function x() { }
for ($i = 0; $i < 1000000; $i++) { @x(); }
?>
real 0m13.333s
user 0m12.437s
sys 0m0.836s
应尽可能避免错误抑制,因为它不仅抑制了您试图阻止的错误,还会抑制您从未预测到会发生的错误。这将使调试成为一场噩梦。
最好在继续运行代码之前测试您知道会导致错误的条件。这样,只有您知道的错误会被抑制,而不是与该代码段相关的所有未来错误。
可能存在使用彻底的错误抑制而不是我建议的方法的充分理由,但是在我多年开发 Web 应用程序的过程中,我还没有遇到过它是良好解决方案的情况。本手册页面上给出的示例当然不是应该使用错误控制运算符的情况。
没有理由仅仅因为“它可能被误用”而不使用某些东西。您也可以说“unlink 是邪恶的,您可以用它删除文件,所以永远不要使用 unlink”。
@ 运算符隐藏所有错误是一个有效的观点——所以我的经验法则是:仅当您知道表达式可能抛出的所有可能的错误,并且您认为所有这些错误都无关紧要时才使用它。
一个简单的例子是
<?php
$x = @$a["name"];
?>
这里只有两个可能的问题:缺少变量或缺少索引。如果您确定这两个情况都可以接受,那么您就可以继续使用它了。再说一遍:抑制错误不是犯罪。不知道何时可以安全地抑制它们肯定更糟糕。
在PHP8的错误处理程序中仍然可以检测到何时使用了@运算符。如文档所述,调用error_reporting()将不再返回0,但是使用@运算符仍然会更改调用error_reporting()时的返回值。
我的PHP错误设置设置为使用E_ALL,当我在非抑制错误的错误处理程序中调用error_reporting()时,它按预期返回E_ALL。
但是,当在尝试使用@运算符抑制错误的表达式上发生错误时,它返回:E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR(或数字4437)。
我不想在我的代码中使用4437,以防它在不同的设置或将来的PHP版本中发生变化,所以我现在使用
<?php
function my_error_handler($err_no, $err_msg, $filename, $linenum) {
if (error_reporting() != E_ALL) {
return false; // 静默
}
// ...
}
?>
如果代码需要与所有版本的PHP一起使用,您可以检查error_reporting()是否不等于E_ALL或0。
当然,如果您的PHP中error_reporting设置不是E_ALL,则必须将其更改为您使用的任何设置。
快速调试方法
@print($a);
等价于
if isset($a) echo $a ;
@a++;
等价于
if isset($a) $a++ ;
else $a = 1;
请注意,此运算符的行为从php5更改为php7。
以下代码将始终引发致命错误,并且您将无法抑制它
<?php
function query()
{
$myrs = null;
$tmp = @$myrs->free_result();
return $tmp;
}
var_dump(query());
echo "THIS IS NOT PRINT";
?>
更多信息请访问: https://bugs.php.net/bug.php?id=78532&thanks=3
在经过一段时间的研究,了解为什么我仍然收到应该使用@抑制的错误后,我发现了以下内容。
1. 如果您设置了自己的默认错误处理程序,则无论@符号如何,错误仍然会发送到错误处理程序。
2. 正如下面提到的,@抑制只会更改该调用的错误级别。这并不是说在您的错误处理程序中您可以检查给定的$errno是否为0,因为$errno仍然指的是错误的类型(而不是错误级别),例如E_WARNING或E_ERROR等
3. @仅将该单个调用的运行时错误报告级别更改为0。这意味着在您的自定义错误处理程序中,您可以使用error_reporting()检查当前的运行时错误报告级别(请注意,如果您想获取当前值,则必须不要向此函数传递任何参数),如果为零,则表示它已被抑制。
<?php
// 自定义错误处理程序
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if ( 0 == error_reporting () ) {
// 错误报告当前已关闭或使用@抑制
return;
}
// 在此处执行正常的自定义错误报告
}
?>
有关设置自定义错误处理程序的更多信息,请参阅: https://php.net/manual/en/function.set-error-handler.php
有关error_reporting的更多信息,请参阅: https://php.net/manual/en/function.error-reporting.php
如果您使用ErrorException异常进行统一的错误管理,我建议您在错误处理程序中测试error_reporting,而不是在异常处理程序中测试,因为您可能会遇到一些麻烦,例如空白页面,因为error_reporting可能不会传递到异常处理程序。
所以,而不是
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline )
{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");
function catchException($e)
{
if (error_reporting() === 0)
{
return;
}
// 执行一些操作
}
set_exception_handler('catchException');
?>
最好这样做
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline )
{
if (error_reporting() === 0)
{
return;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");
function catchException($e)
{
// 执行一些操作
}
set_exception_handler('catchException');
?>
最好使用函数trigger_error() (http://de.php.net/manual/en/function.trigger-error.php)
来显示定义的通知、警告和错误,而不是自己检查错误级别。这允许您将消息写入php.ini中定义的日志文件,输出
消息依赖于error_reporting()级别,并使用@符号抑制输出。
虽然您绝对不应该过度使用@运算符,但我也不同意那些声称它是最终罪恶的人。
例如,一个非常合理的用法是抑制parse_ini_file()生成的通知级错误,如果您知道.ini文件可能丢失。
在我的情况下,获取FALSE返回值足以处理这种情况,但我不想让我的API输出通知错误。
TL;DR:使用它,但仅当您知道您正在抑制什么以及为什么时。
当表达式遇到错误时,PHP对于分配受错误控制运算符保护的表达式的返回值的变量的行为是什么?
根据以下代码,结果为NULL(但如果在所有情况下都确认这一点会很好)。
<?php
$var = 3;
$arr = array();
$var = @$arr['x']; // 此赋值后$var的值是什么?
// 它是否为其先前值(3),就像赋值从未发生过一样?
// 它是否为FALSE或NULL?
// 它是否为某种异常或错误消息或错误编号?
var_dump($var); // 打印“NULL”
?>
* 如何使已弃用的超级全局变量`$php_errormsg`工作
>1. 修改php.ini
>track_errors = On
>error_reporting = E_ALL & ~E_NOTICE
>2. 请注意,如果您已经使用自定义错误处理程序,它将提示“未定义变量”
>请在执行代码之前插入代码`set_error_handler(null);`,例如
>```php
>set_error_handler(null);
>$my_file = @file ('phpinfo.phpx') or
>die ("<br>Failed opening file: <br>\t$php_errormsg");
>```
>(c)Kenny Fang