我对 @ 符号的实际作用感到困惑,经过几次实验后,得出以下结论
* 无论错误报告级别设置为什么,或者语句前面是否加了 @,设置的错误处理程序都会被调用
* 由错误处理程序赋予不同错误级别的意义。您可以使自定义错误处理程序回显所有错误,即使错误报告设置为 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
vs
<?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() 检查当前的运行时 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