nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

PHPの三項演算子がネストするとおかしくなる話と対策

PHP三項演算子がネストするとおかしくなる

Perlと同じのりで三項演算子を使おうとするとはまれる。

検証コード

<?php
    function fizzbuzz($num) {
        return ($num % 15 == 0) ? 'FizzBuzz' 
             : ($num %  5 == 0) ? 'Fizz'
             : ($num %  3 == 0) ? 'Buzz' 
             :                    $num
             ;
    }
    for ($i = 1; $i < 20; $i++) {
        echo fizzbuzz($i) . "\n";
    }
?>

結果

1
2
Buzz
4
Buzz
Buzz
7
8
Buzz
Buzz
11
Buzz
13
14
Buzz
16
17
Buzz
19

http://d.hatena.ne.jp/omoon/20071127/1196174396
とか
http://d.hatena.ne.jp/pasela/20080524/ternary
とかで指摘されている話。

論理演算子で逃げようとしたけどダメ

PHPの論理演算子はboolしか返さないので、これもPerlと同じのりで使おうとするとはまれる。

検証コード

<?php
    function fizzbuzz($i, $num, $str) {
        return ($i % $num == 0 ) ? $str : false;
    }
    for ($i = 1; $i < 20; $i++) {
        $val = fizzbuzz($i, 15, 'FizzBuzz') || fizzbuzz($i, 5, 'Fizz') || fizzbuzz($i, 3, 'Buzz') || $i;
        echo $val . "\n";
    }
?>

結果

1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

対策

http://phpspot.org/blog/archives/2007/12/phpifelse3.html
に書いてある感じで1行にまとめると、括弧の対応がパッと見でわかんなくなるのでキモい。

なので、行頭を揃えるのを諦めて、評価順通りに括弧をつけて改行とインデントするのがベターかな。

検証コード

<?php
    for ($i = 1; $i < 20; $i++) {
        $val = 
            ($i % 15 == 0) ? 'FizzBuzz' : (
                ($i %  5 == 0) ? 'Fizz' : (
                    ($i %  3 == 0) ? 'Buzz' : $i
                )
            );
        echo( $val . "\n" );
    }
?>

結果

1
2
Buzz
4
Fizz
Buzz
7
8
Buzz
Fizz
11
Buzz
13
14
FizzBuzz
16
17
Buzz
19

対策その2

もう少し調べてみたら、?:演算子がPerlのor演算子的に動いてくれるらしい。

検証コード

<?php
    function fizzbuzz($val, $num, $str) {
        return ($val % $num == 0) ? $str : false;
    }
    foreach (range(1, 19) as $i) {
        $val = fizzbuzz($i, 15, 'FizzBuzz') ?: fizzbuzz($i, 5, 'Fizz') ?: fizzbuzz($i, 3, 'Buzz') ?: $i;
        echo $val . PHP_EOL;
    }
?>

結果

1
2
Buzz
4
Fizz
Buzz
7
8
Buzz
Fizz
11
Buzz
13
14
FizzBuzz
16
17
Buzz
19