PHP Conference Japan 2024

錯誤控制運算子

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 (
"開啟檔案失敗:錯誤為 '" . error_get_last()['message'] . "'");

// 這適用於任何表達式,而不僅僅是函式:
$value = @$cache[$key];
// 如果索引 $key 不存在,則不會發出通知。

?>

注意 @ 運算子僅適用於表達式。一個簡單的經驗法則是:如果可以取得某個事物的數值,那麼就可以在其前面加上 @ 運算子。例如,它可以加在變數、函式呼叫、某些語言結構呼叫(例如 include)等等的前面。它不能加在函式或類別定義,或是條件結構(例如 ifforeach)等等的前面。

警告

在 PHP 8.0.0 之前,@ 運算子可能會停用將終止腳本執行的嚴重錯誤。例如,在不存在(由於不可用或拼寫錯誤)的函式呼叫前面加上 @ 會導致腳本終止,而沒有任何原因指示。

新增註解

使用者提供的註解 16 個註解

236
taras dot dot dot di at gmail dot com
16 年前
我對 @ 符號的實際作用感到困惑,經過幾次實驗後,得出以下結論

* 無論錯誤回報設定為何,或是陳述式前面是否帶有 @,所設定的錯誤處理器都會被呼叫

* 由錯誤處理器來賦予不同錯誤等級一些意義。您可以使自訂錯誤處理器回顯所有錯誤,即使錯誤回報設定為 NONE。

* 那麼 @ 運算子有什麼作用?它會暫時將該行的錯誤回報等級設定為 0。如果該行觸發錯誤,錯誤處理器仍然會被呼叫,但它會以錯誤等級 0 被呼叫

希望這對某人有幫助
143
M. T.
15 年前
請注意在 include() 之前的陳述式中使用錯誤控制運算子,如下所示

<?PHP

(@include("file.php"))
OR die(
"找不到 file.php!");

?>

這會導致包含檔案的錯誤回報等級也設定為零。因此,如果包含的檔案中存在一些錯誤,它們將不會顯示。
45
Anonymous
11 年前
這個運算子被資深的 PHP 開發人員親切地稱為 stfu 運算子。
60
anthon at piwik dot org
13 年前
如果您想知道使用 @ 運算子的效能影響,請考慮以下範例。在此,第二個腳本(使用 @ 運算子)的執行時間是第一個腳本的 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
54
gerrywastaken
15 年前
如果可能,應該避免抑制錯誤,因為它不僅會抑制您試圖阻止的錯誤,還會抑制您沒有預料到會發生的錯誤。這會使除錯成為一場惡夢。

最好在繼續執行程式碼之前,測試您知道會導致錯誤的條件。這樣一來,只會抑制您知道的錯誤,而不會抑制與該段程式碼相關的所有未來錯誤。

使用我建議的方法,而非直接抑制錯誤,可能會有充分的理由。然而,在我編寫網頁應用程式的多年經驗中,我從未遇到過這是個好解決方案的情況。本手冊頁面給出的範例,絕對不是應該使用錯誤控制運算子(@)的情況。
33
dkellner
8 年前
不應該因為「可能會被濫用」而不使用某個東西。你也可以說「unlink 很邪惡,你可以用它刪除檔案,所以永遠不要使用 unlink」。

@ 運算子會隱藏所有錯誤,這是一個有效的觀點 - 所以我的經驗法則是:只有在你清楚你的表達式可能會拋出的所有錯誤,並且你認為所有這些錯誤都不重要時,才使用它。

一個簡單的例子是
<?php

$x
= @$a["name"];

?>
這裡只有兩個可能的問題:變數遺失或索引遺失。如果你確定這兩種情況都可以接受,那就沒問題。再次強調:抑制錯誤並不是犯罪。不清楚何時可以安全地抑制錯誤,肯定更糟糕。
16
Ryan C
3 年前
在 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 錯誤報告設定不是 E_ALL,你必須將其更改為你使用的任何設定。
15
man13or at hotmail dot fr
5 年前
快速除錯方法

@print($a);
等同於
if isset($a) echo $a ;

@a++;
等同於
if isset($a) $a++ ;
else $a = 1;
7
jcmargentina at gmail dot com
5 年前
請注意,此運算子的行為從 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
20
darren at powerssa dot com
14 年前
在花了一些時間研究為什麼我仍然收到應該用 @ 抑制的錯誤後,我發現了以下內容。

1. 如果你設定了自己的預設錯誤處理程序,那麼無論是否有 @ 符號,錯誤仍然會傳送到錯誤處理程序。

2. 如下所述,@ 抑制只會針對該呼叫變更錯誤層級。這並不是說在你的錯誤處理程序中,你可以檢查給定的 $errno 是否為 0,因為 $errno 仍然會參照錯誤的 TYPE(而非錯誤層級),例如 E_WARNING 或 E_ERROR 等。

3. @ 只會將該次呼叫的執行階段錯誤報告層級變更為 0。這表示在你的自訂錯誤處理程序中,你可以使用 error_reporting() 檢查目前的執行階段 error_reporting 層級(請注意,如果你想取得目前值,則不得將任何參數傳遞給此函式),如果它是零,則表示它已被抑制。
<?php
// 自訂錯誤處理程序
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (
0 == error_reporting () ) {
// 錯誤報告目前已關閉或使用 @ 抑制
return;
}
// 在這裡執行你的正常自訂錯誤報告
}
?>

如需設定自訂錯誤處理程序的更多資訊,請參閱:https://php.dev.org.tw/manual/en/function.set-error-handler.php
如需 error_reporting 的更多資訊,請參閱:https://php.dev.org.tw/manual/en/function.error-reporting.php
18
auser at anexample dot com
14 年前
請注意,使用 @ 的速度非常慢,因為 PHP 在以這種方式抑制錯誤時會產生額外負荷。這是在速度和便利性之間的權衡。
11
bohwaz
13 年前
如果你使用 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');

?>
8
frogger at netsurf dot de
19 年前
最好使用函式 trigger_error() (http://de.php.net/manual/en/function.trigger-error.php)
來顯示已定義的通知、警告和錯誤,而不是自己檢查錯誤層級。如果 php.ini 中有定義,這可讓你將訊息寫入日誌檔,並根據 error_reporting() 層級輸出
訊息,並使用 @ 符號抑制輸出。
4
karst dot REMOVETHIS at onlinq dot nl
9 年前
雖然你絕對不應該太過自由地使用 @ 運算子,但我也不贊成那些聲稱它是終極罪惡的人。

例如,一個非常合理的用法是抑制 parse_ini_file() 在你已知 .ini 檔案可能遺失時產生的通知層級錯誤。
在我的情況下,取得 FALSE 傳回值足以處理該情況,但我不想讓我的 API 輸出通知錯誤。

TL;DR:使用它,但前提是你清楚自己在抑制什麼以及為什麼要抑制。
1
ricovox
7 年前
當表達式遇到錯誤時,如果該表達式被錯誤控制運算符保護,則將該表達式的返回值賦值給變數時,PHP 的行為是什麼?

根據以下程式碼,結果是 NULL (但如果能確認在所有情況下都為真,那就太好了)。

<?php

$var
= 3;
$arr = array();

$var = @$arr['x']; // 在此賦值之後,$var 的值是什麼?

// 它的值會像從未進行賦值一樣,保持先前的的值 (3) 嗎?
// 它是 FALSE 還是 NULL?
// 它會是某種例外、錯誤訊息或錯誤編號嗎?

var_dump($var); // 列印 "NULL"

?>
0
fy dot kenny at gmail dot com
4 年前
* 如何讓已棄用的超全域變數 `$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>開啟檔案失敗:<br>\t$php_errormsg");
>```

>(c) Kenny Fang
To Top