PHPの三項演算子の注意すべき挙動
PHPで以下のようなコードを書いていてしばらくはまっていたのでメモ。
<?php function a($a) { printf("%s\n", isset($a["A"]["B"]) ? $a["A"]["B"] : isset($a["AA"]["BB"]) ? $a["AA"]["BB"] : "CCC"); } $a = array(); $a["A"]["B"] = "C"; a($a); // "C" ? $a = array(); $a["AA"]["BB"] = "CC"; a($a); // "CC" $a = array(); a($a); // "CCC"
実行結果は以下の通り。
PHP Notice: Undefined index: AA in boo.php on line 8 CC CCC
8行目でNoticeが出ることがある。8行目は
$a["AA"]["BB"] :
の部分‥7行目でissetでチェックしているはずだが‥
ググってみると、以下のようなものが見つかった。
曰く
注意:
三項演算子を “積み重ねて” 使用することは避けましょう。 ひとつの文の中で複数の三項演算子を使用した際の PHP の振る舞いは、 少々わかりにくいものです。
例として挙がっているのが以下のコード。
<?php echo (true?'true':false?'t':'f');
これはPHPでは
<?php echo((true ? 'true' : false) ? 't' : 'f');
と評価され、’t' が出力されると。
‥ってこんなん分かるかいっ‥ってことで、各言語で試してみた。
PHP
<?php printf("%s\n", (true ? 'true' : false ? 't' : 'f')); printf("%s\n", (false ? 'false' : true ? 't' : 'f')); printf("%s\n", (false ? 'false' : false ? 't' : 'f'));
実行結果
t t f
Java
CentOS 7のOpenJDK 1.8.0_121
public class Hoge { public static void main(String[] args) { System.out.println(true ? "true" : false ? "t" : "f"); System.out.println(false ? "false" : true ? "t" : "f"); System.out.println(false ? "false" : false ? "t" : "f"); } }
実行結果
true t f
C
#include <stdio.h> #include <stdbool.h> void main(int argc, char** argv) { printf("%s\n", true ? "true" : false ? "t" : "f"); printf("%s\n", false ? "false" : true ? "t" : "f"); printf("%s\n", false ? "false" : false ? "t" : "f"); }
実行結果
true t f
C++
#include <iostream> using namespace std; int main(int argc, char** argv) { cout << (true ? "true" : false ? "t" : "f") << endl; cout << ("%s\n", false ? "false" : true ? "t" : "f") << endl; cout << ("%s\n", false ? "false" : false ? "t" : "f") << endl; return 0; }
実行結果
true t f
Ruby
printf("%s\n", true ? "true" : false ? "t" : "f"); printf("%s\n", false ? "false" : true ? "t" : "f"); printf("%s\n", false ? "false" : false ? "t" : "f");
実行結果
true t f
Python
print('true' if True else 't' if False else 'f'); print('false' if False else 't' if True else 'f'); print('false' if False else 't' if False else 'f');
実行結果
true t f
参考
結局‥
冒頭のコードは
<?php function a($a) { printf("%s\n", isset($a["A"]["B"]) ? $a["A"]["B"] : (isset($a["AA"]["BB"]) ? $a["AA"]["BB"] : "CCC")); } $a = array(); $a["A"]["B"] = "C"; a($a); // "C" $a = array(); $a["AA"]["BB"] = "CC"; a($a); // "CC" $a = array(); a($a); // "CCC"
のように括弧を付けて逃げましたとさ。やれやれ。