PHP のコードで、@file_get_contents(...) や @$_GET['id'] のように、関数や変数の前に @ が付いているのを見かけることがあります。これはエラー制御演算子と呼ばれるもので、その式で出るエラーメッセージを表示しないようにします。手軽に警告を消せますが、多用すると、不具合の原因を探すのがとても難しくなります。
@ がすること
<?php
// ファイルが無いと「Failed to open stream」という警告が出る
$text = file_get_contents('/path/to/missing.txt');
// @ を付けると、警告は出ない (失敗して false が返るのは同じ)
$text = @file_get_contents('/path/to/missing.txt');
var_dump($text); // bool(false)
@ は、関数の呼び出しだけでなく、変数や配列の要素、include など、値を返す式の前に付けられます。if や foreach の前には付けられません。
大事なのは、@ が消すのはメッセージだけ、という点です。ファイルが読めなかったという事実は変わらず、関数は false を返します。戻り値を確かめずに先へ進むと、ずっと後の処理で別の不具合として表に出ます。そのときには、もとの警告は画面にもログにも残っていないため、原因にたどり着くのに時間がかかります。
PHP 8 での変更
PHP 8.0 で、@ の扱いが変わりました。
- 致命的なエラー (スクリプトが止まるもの) は、
@で隠れなくなった。以前は、存在しない関数の呼び出しなどに@が付いていると、何も表示されないままスクリプトが止まることがあった set_error_handler()で自作のエラーハンドラーを登録していると、@が付いた式のエラーでもハンドラーが呼ばれる (これは以前から同じ)。その中でerror_reporting()を呼ぶと、以前は0が返ったが、PHP 8.0 からは致命的なエラーの種類 (E_ERRORなど) を合わせた値が返る。error_reporting() === 0で@の有無を見分けていたハンドラーは、書き直しが必要
また、PHP 8 では、関数に違う型の引数を渡したときなど、多くの場面で警告ではなく例外 (TypeError や ValueError) が投げられるようになりました。例外は @ では止められません。
代わりの書き方
戻り値を確かめる
失敗すると false を返す関数は、=== で確かめます。== で比べると、空のファイル ('') と失敗 (false) を区別できません。
<?php
$text = file_get_contents($path);
if ($text === false) {
// 読めなかったときの処理
}
警告そのものを出したくないなら、先に is_file() や is_readable() で確かめます。ただし、確かめてから読むまでの間にファイルが消されることもあるので、戻り値の確認は省きません。
無いかもしれない配列の要素は ?? で読む
@$_GET['page'] のように、無いかもしれない配列の要素を読むために @ を使っている場合は、?? (null 合体演算子) で置き換えられます。左側が無いか null のとき、右側の値になります。
<?php
$page = (int) ($_GET['page'] ?? 1);
エラーを例外に変える
警告を例外に変えておくと、try と catch で失敗を扱えます。多くのフレームワークも、同じような仕組みでエラーを例外として扱っています。
<?php
set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
if (!(error_reporting() & $severity)) {
// 報告しない設定のエラー (@ が付いたものを含む) は、PHP の通常の処理に任せる
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
try {
$text = file_get_contents($path);
} catch (ErrorException $e) {
error_log('読み込みに失敗しました: ' . $e->getMessage());
$text = null;
}
どうしても @ を使うなら、すぐに error_get_last() を見る
ほかに手段が無く @ を使うときは、直後に error_get_last() で、隠したエラーの内容を取り出して記録します。@ で隠したエラーも、この関数で読めます。次のエラーで上書きされるので、すぐに確かめます。
<?php
$text = @file_get_contents($path);
if ($text === false) {
$error = error_get_last();
error_log('読み込みに失敗しました: ' . ($error['message'] ?? '不明なエラー'));
}
利用者にエラーを見せたくないとき
@ を使う理由が「利用者にエラーメッセージを見せたくない」なら、php.ini の設定で行うのが正しい方法です。本番では display_errors を Off にして画面に出さず、log_errors を On にしてログで確認します。開発の環境では display_errors を On にして、すぐに気づけるようにします。
; 本番の php.ini の例
display_errors = Off
log_errors = On
error_reporting = E_ALL
エラーメッセージには、ファイルのパスなど、外に見せたくない情報が含まれることがあります。表示を止めるのは、コードの @ ではなく設定の役目です。
まとめ
@は、その式で出るエラーメッセージを表示しない演算子。失敗そのものは無くならない- PHP 8.0 からは、致命的なエラーは
@で隠れない。例外も@では止まらない - 代わりに、戻り値を
===で確かめる、??を使う、エラーを例外に変えてtryとcatchで扱う - 使うなら、直後に
error_get_last()で内容を記録する - 利用者に見せたくないなら、
display_errorsを Off、log_errorsを On にする