読者です 読者をやめる 読者になる 読者になる

HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

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: 比較演算子 - Manual

曰く

注意:
三項演算子を “積み重ねて” 使用することは避けましょう。 ひとつの文の中で複数の三項演算子を使用した際の PHP の振る舞いは、 少々わかりにくいものです。

例として挙がっているのが以下のコード。

<?php
echo (true?'true':false?'t':'f');

これはPHPでは

<?php
echo((true ? 'true' : false) ? 't' : 'f');

と評価され、’t' が出力されると。

‥ってこんなん分かるかいっ‥ってことで、各言語で試してみた。

PHP

CentOS 7のPHP 5.4.16

<?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

CentOS 7のgcc バージョン 4.8.5

#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++

CentOS 7のgcc バージョン 4.8.5

#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

CentOS 7のruby 2.0.0p648

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

CentOS 7のPython 2.7.5

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

参考

pythonで三項演算子のネスト - Qiita

結局‥

冒頭のコードは

<?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"

のように括弧を付けて逃げましたとさ。やれやれ。