弊社ではエンジニアの方がジョインする時に業務経歴書とあわせてご本人が書いたプログラムを見せて頂いています。
そんなプログラム群の中にこんなコードがありました。(内容は変更しています。)
[php]
$fp = fopen(‘data.txt’, ‘r’) or die(‘file open error’);
[/php]
この構文は割と有名な構文ですが、ふと「どういう理屈で解釈されているんだろう」と気になりました。
最初はisset()みたいに利便性優先の構文だろうと思ったけど、考えてみるとこれは通常のPHPの「式」でした。
これを理解するためにはいくつかの前提条件が必要なので純に解説します。
PHPの代入
[php]
$message = ‘this is a pen’;
[/php]
これはPHPプログラマなら誰でも知っている変数への代入です。
[php]
$foo = $bar * 3;
[/php]
こんなのもありますね。
後者だとわかりやすいですが、この構文は「=の右の『式』を評価し、=の左の変数に代入する」という構文です。
前者の例は「文字列」という「式」なのですね。
[php]
$foo = bar();
[/php]
これも「式」。
代入文も「式」として評価され、その評価値は代入した値になります。
なので、
[php]
$foo = $bar = ‘string’;
[/php]
こんなこともできます。
この場合はまず
[php]
$bar = ‘string’;
[/php]
の右辺が評価され、式の値は文字列としての ‘string’ になります。
評価された右辺は左辺の $bar に代入され、代入式の値は代入した文字列、’string’ になります。
そしてその文字列は $foo に代入され一連の処理が完了します。
true と false
PHPでは式がtrueかfalseかで判断するケースが多くあります。例えばこんな。
[php]
if (式){ echo("true"); }
[/php]
式がそのものずばりの true や false を返せば良いのですが、文字列やオブジェクトを返してきた時にどう判断するか。
PHPではざっくり以下の様になっています。
falseと見なされる | false, 0, “0”, 空文字, null |
---|---|
trueと見なされる | 0でない数字、”0″でない文字列などその他の全て |
論理演算式の評価
どんな言語にも or や and といった論理演算子があります。
最初の例で出てくる or もこの論理演算子ですね。
論理演算子はその左の式と右の式を論理演算することになりますがPHPでは最小限の評価で済む様に式の評価を行います。
これはPHPによらず多くの言語でそうなっています。
and はその左の式と右の式が両方 true の時に式全体が true を返します。つまり片方が false の場合もう片方の値に関わらず式全体は false になります。
PHPでは and の左の式を評価してその結果が false だった場合、右の式を評価せず false となります。
逆に or の場合、左の式を評価してその結果が true だった場合、右の式を評価せず true となります。
そんで?
最初の式に戻りましょう。
[php]
$fp = fopen(‘data.txt’, ‘r’) or die(‘file open error’);
[/php]
これ。
fopen()はファイルのオープンに成功するとファイルポインタリソースを返します。これは前述の「trueとfalse」の例で言うとfalseでも0でも”0″でも空文字でもnullでもないのでtrueと見なされます。一方、ファイルのオープンに失敗するとfalseを返します。こっちは問答無用のfalseですね。
この構文を見つけるとPHPはまず左の代入文を処理します。
ファイルのオープンに成功すると $fp にファイルポインタリソースが代入され、式全体の値は true になります。このとき or の左の式が true になっているのでPHPは右の式を評価しません。つまり、die() は実行されません。
一方、ファイルのオープンに失敗した場合 $fp に false が代入され、式全体の値は false になります。or の左の式が false なのでPHPは右の式を評価する必要があるので die() が実行され、プログラムはエラーメッセージとともに停止します。
こんな訳でこの構文は「ファイルをオープンし $fp にファイルポインタを格納する。ファイルのオープンに失敗した場合はプログラムを終了する。」となる訳ですね。
仕事でプログラムを作る時など様々なレベルのプログラマがコードを読む状況では以下の様に書いた方が「可読性」は高いと思います。
[php]
if (! ($fp = fopen(‘data.txt’, ‘r’))){ die(‘file open error’); }
[/php]
代入文が返す値をそのまま評価する形もマニアックと言えばマニアックですのでチーム構成や運用体制によってさらに平易な書き方をしても良いでしょう。
[php]
$fp = fopen(‘data.txt’, ‘r’);
if ($fp === false){ die(‘file open error’); }
[/php]
締め
何気ない構文でも奥が深いものですね。
PHPは前述の通りtrue, falseと見なされる値が広いなど、なんとなくテキトーに書いてもうまく動くことが多いのです。
でもそれを「なんとなく」のままにしておくとちょっとした仕様変更で地獄を見ることがあります。
そんな状況にならない様に常に「なぜ自分が書いたプログラムが動いているか」を意識しておくことが重要です。
同じ様なネタとしては「PHPの比較」というのもありますね。if (0 == “”){} が成立するとか。
これについてはまたの機会に。