PHPのand,or演算子の使い道を考えてみる

突然ですが、PHPの問題です。

<?php

$a = false and false;
$a = true and false;
$a = true and true;

$a = true or true;
$a = true or false;
$a = false or true;
$a = false or false;

おなじみのand, or演算子の優先順位テストです。
それぞれの$aには何が代入されているでしょうか。

答え

答えは以下の通りです。

$a = false and false => false
$a = true and false  => true
$a = true and true   => true
$a = true or true    => true
$a = true or false   => true
$a = false or true   => false
$a = false or false  => false

だいぶ気持ち悪いですね。

特に、

  • $a = true and false=> true
  • $a = false or true=> false

上記2つは初見殺しにも程が有ります。

なぜこのような問題が起こるかというと、
and, or演算子は=よりも優先度が低いため、

($a = true) and false;  // 後ろのfalseは評価されるだけで$aに影響しない
($a = false) or true;   // 後ろのtrueは評価されるだけで$aに影響しない

評価される際には、このような式になっているためです。

このように非常に難解な動きをするand, or演算子ですが、
論理式とは少し違う使い方を見つけたので、メモを残しておきます。

面白い書き方

and, orは論理演算子なので基本的にif文やwhile等の評価式の中に使用します。
しかし、評価式として使用しない使用方法があるようです。

<?php

function p($str) { echo $str; }

true and p("true andn");
false and p("false andn");

true or p("true orn");
false or p("false orn");

こんなコードを用意してみました。
このコードを実行すると何が出力されるでしょう。

true and
false or

はい。どうでしょうか。

  • andの手前の式がtrueと評価される場合には後ろの式が実行され、
  • orの手前の式がfalseと評価される場合には後ろの式が実行され、
  • それ意外の場合は後ろの式が実行されない

という書き方ができます。

※ ちなみにechoをそのまま書くと構文エラーになります
PHP Parse error: syntax error, unexpected 'echo' (T_ECHO)

実用例

(実用的かどうか微妙な例ですが、)実際にある場面を例にあげます。

構文の都合上、関数の引数のデフォルト値に指定できない値
例えばクロージャ(function() {})などを初期化する際には、

function hoge($fn = null) {
    if(is_null($fn)) $fn = function() {};

    return $fn;
}

と書くことがあると思います。 こんな時に、andを使うことで、初期化の式を少しだけシンプルに出来ます。

function foo($fn = null) {
    is_null($fn) and $fn = function() {};

    return $fn;
}

実際に試してみると、

var_dump(hoge());   // class Closure#1 (0) {}

var_dump(foo());    // class Closure#1 (0) {}

初期化できています。

ちなみに、意味が分かりづらいので個人的には嫌いですが、 更にタイプ量を削るなら、デフォルト値をnullでなくfalseにしておき、

function foo($fn = false) {
    $fn or $fn = function() {};

    return $fn;
}

orを使ってこう書くこともできます。
結局トリッキーさは抜けませんが、慣れれば案外見やすい書き方かもしれません。

トリッキーなので自分は使いませんが、こんな使い方もできるそうです。
というご紹介でした。

あとがき

ちなみに&&||でも同じことができます。
あれ、その書き方どこかで・・・

function hoge(fn) {
    fn = fn || function() {};
}

jsはちょっと違いましたね。