【PHP】エラー制御演算子に正しい使い道はあるのか?

エディタPHP

こんにちは。いっとくです!

現在PHPを使ったWebサイト運用の現場にいるのですが、ちらっとソースコードの中にエラー制御演算子を見つけました。

@“←この普段はメールアドレスかSNSのアカウントでしか見かけないこの記号です。

エラー制御演算子とは名前の通り、@がついている箇所でエラーが発生しそうになったら抑え込んで制御するという演算子。

普段業務をしていてもあまり見かけることがないので、具体的にどんなときに使うんだろうと思っって調べてみると、中々風当たりが強い記法のようです。

参考:PHP プログラマが “@” を使うべきでない 5つの理由

否定的な意見が出てくる原因は、どんなレベルのエラーも丸っと封じ込めることができてしまうので、この箇所が原因でバグが出てしまった時に、原因を追求できなくなってしまうから。

PHPのマニュアルにもしっかり警告がつけられています。

警告 現在、エラー制御演算子プレフィックス”@”は、スクリプトの実行を 終了するような致命的なエラーの出力さえ抑圧します。このため、ある関数の エラー出力を抑制するために “@” を使用した場合、その関数が 利用できなかったり、ミスタイプがあった場合でも、原因を示すことなく その場所でスクリプトは終了してしまいます。

PHPマニュアル – エラー制御演算子より

ここまで警戒されていると使うのも躊躇してしまいますが、わざわざこの書き方が存在しているってことは、何かしら使うと便利なタイミングがあるんじゃないの??という素朴な疑問。

合わせて現在触っているソース内で使われていたエラー制御演算子が、どう考えても重大な問題を引き起こすようにも見えなかったので、ちょっとその辺をまとめてみようと思います。

スポンサーリンク

エラー制御演算子はなぜ問題なのか

エラー制御演算子が問題になるケースは、エラーが出ていないのにプログラムがちゃんと動いており、かつ想定していない結果を吐き出してしまうということだと思います。

使い方によっては定義されていない変数のエラーが押さえ込まれて、本来だったらundefinedエラーが出るところにnullが代入されてしまう的なことがあるわけです。

例えば以下のコード

<?php
$pref = 'hokkiado';
$population = getPopulation($pref);

echo $pref . 'の人口は' . $population . '人です';

function getPopulation($pref) {
    if ($pref === 'tokyo') {
        $population = 10000000;
    } elseif ($pref === 'hokkaido') {
        $population = 2000000;
    }
    return $population;
}

変数$prefにhokkaidoかtokyoを渡すと人口を取り出してくれるような関数です。

このコードを実行すると、以下のような実行結果になります。

Notice: Undefined variable: population in /Applications/MAMP/htdocs/PerfectPHP/outputs/constInClass.php on line 12
hokkiadoの人口は人です

一見正しく動きそうですが、$populationが定義されていませんよとのこと。

原因を辿ってみると、$prefの時点でhokkaidoのスペルをhokkiadoと打ち間違えています。

それが原因でgetPopulation関数内でどちらの分岐処理にも当てはめられず、$populationが未定義のままになります。

通常の作業ではこのエラーを解決しようとしてソースを辿り、原因を解消すると思います。

しかし、ここではとりあえずエラーを封じ込めるために、エラーが発生している箇所にエラー制御演算子を使ってみましょう。

<?php

$pref = 'hokkiado';
$population = getPopulation($pref);

function getPopulation($pref) {
    if ($pref === 'tokyo') {
        $population = 10000000;
    } elseif ($pref === 'hokkaido') {
        $population = 2000000;
    }
    return @$population;
}

echo $pref . 'の人口は' . $population . '人です';

そして、これを実行すると以下の文が出力されます。

hokkiadoの人口は人です

なんかエラーは出てないけど、どう見ても正しく動いていませんね。

これが今書いたみたいな簡単なプログラムであればいいのですが、複雑なプログラムのどこかにさりげなく埋め込まれていたらと考えるとデバッグするのも簡単ではありません。

では、エラー制御演算子はどんな場合でも必ず悪なのでしょうか!?

エラー制御演算子を使ってもいいケースとは

エラーというのはプログラミングを始めたての時期の方にとっては、またエラー出た!こんちきしょう!というストレスや、全然エラー直らないせいでクソつまらんわ!というようなストレスになります。

そうです。つまるところ全てのエラーがストレスの方向に向かっていってしまうわけです。

僕もセブ島でプログラミングの勉強をしていた時は、エラーの内容がわからなすぎてめっちゃイライラしてました。

エラー画面が仰々しすぎるんじゃ!

しかし不思議なことにプログラミングに慣れてくると、むしろエラーが出てない方がストレスになります。

エラー出てないのに正しく動いてない!みたいな

この場合、原因がすぐに特定できないためストレスに繋がります。

余談ですが、このエラーのなさから僕はHTMLとかCSSがあまり好きではありません。どうでもいいですね。

ということで状況によっては毒にも薬にもなるエラーですが、自分でエラーのハンドリングとかを設定した部分はさておき、全てのエラーはPHPというプログラムの中に組み込まれています。

そしてこれはプログラムを動かすために必要なルールをつけてくれているということなので、すごくありがたいことです。

でもプログラミングにとってはエラーだとしても、実際のアプリケーション上の動作的にはエラーじゃないよってものがあれば、その時こそエラー制御演算子の出番なんじゃないでしょうか!?

正直そういった類のシーンって中々お目にかかるものではないと思いますが、先日ソースコードにこんな書き方があったので、ちょっと紹介したいと思います。

<?php
// モデル部分でバリデーションを行い、入力ミスがあれば$errorsにメッセージを代入する
$error = '';

// ビューでの表示部分。もしエラーがあればエラーメッセージを出力する
if (!empty($error)) {
    echo '<p class="errorText">' . $error . '</p>';
}

もうめっちゃ省略して書いてます。モデルとビューの必要な内容だけをぎゅっと詰め込みました。

上の$errorsの部分では本来バリデーションの処理があり、入力の内容を間違えていた場合、$errorsにメッセージが入るというよくある処理です。

そして、ビュー側ではもしエラーがあれば、エラーメッセージを吐き出すといういたって普通のバリデーション。

ここにエラー制御演算子が使われていました。

<?php

$error = '';

echo '<p class="errorText">' . @$error . '</p>';

なんだか見た目はスッキリして見えますね。

個人的にはこの書き方ならわかりやすいかもと思ってしまいました。

というのも、$errorが正しく表示されていない時に見ればいいのはバリデーションの処理で$errorに正しく値が渡されているかどうかの部分だけだから。

バリデーションでそこまで複雑なロジックを組むことも少ないでしょうから、原因不明になるということはないのでは??

そもそも$errorに値が渡されなかった場合に気づきにくいような気もしますが、それはemptyとかissetを使っても同じですからねぇ。

唯一気になる点は、エラー制御演算子を使った場合の方が処理速度が遅いということでしょうか。

ローカルの環境で測ってみたら、場合によってはemptyと比べて10倍くらい速さに差が出ました。

といっても0.0000…秒みたいな世界での10倍なので体感速度に影響はないのですが、ちりも積もってなんとやらということで、積み重ねがアプリケーションを遅くしたりしますからね。

毎日ちょっとずつ体に悪いものを取り込むと、いつか病気になるみたいなマインド。

あとエラー制御演算子は一般的にあまりいいとされていないので、状況に関係なく書き方自体に嫌悪感を抱いてしまう方も一定数いて、「なんて書き方しやがるっ…!」と思われてしまうのも嫌なので、結論、僕はエラー制御演算子を使わない!

以上、あんまり使わないエラー制御演算子について考えてみました。

もし便利な使い方あるよ!って方がいらっしゃったらコメントで教えていただけると嬉しいです!

では!

コメント

タイトルとURLをコピーしました