カンマ演算子って何?
使い方によって正しく動く時とPHP Parse errorが出る時とあるんだけどなんで?
…って話をFacebookで友人の友人が話題にしていたのだけどコメント権限がなかったようで。
とりあえずここにでも書いておこうかと思った次第。
PHP: 演算子の優先順位 - Manual
http://php.net/manual/ja/language.operators.precedence.php
演算子の種類
PHPのドキュメント「演算子の優先順位」で挙げられている演算子は8つに分けられる。
・式と式の間に付く演算子(四則演算子 比較演算子等)
・式の前に付く演算子(Clone演算子 キャスト等)
・クラス名の前に付く演算子(New演算子)
・変数の前後に付く演算子(インクリメント)
・変数と式の間に付く演算子(代入演算子)
・式とクラス名の間に付く演算子(instanceof)
・展開結果が配列変数となるものの後ろ付く演算子(Array演算子) ←特殊
・複数のオペランドを受け取る必要がある場所で使える演算子(カンマ演算子) ←特殊
普通の演算子と特殊な演算子
上の6つは普通の演算子で、その演算子とオペランドを合わせたものが「式」となるもの。
「$a + $b」「(string)$a」「new stdClass」「$a++」「$a = 123」「$a instanceof stdClass」等が式となる。
残り下2つは、演算子とオペランドを合わせても式にならない。
でもArray演算子の場合は例外があって、PHP5.4で追加された配列の短縮構文とかは式になる。
(まぁそれは演算子じゃなくて構文か)
ちなみに、普通/特殊は自分で勝手に分けたので考え方によっては違うかも。
カンマ演算子
少なくともカンマ演算子は普通じゃない。
複数のオペランドを受け取る事が出来るように、その場その場で使えるようにされている。
例えばこんな感じ。
・関数を定義する時の引数部分は、複数の変数を受け取れるにカンマ演算子が使える。
・echo構文の引数部分(関数じゃないから厳密には引数じゃない)は、複数の式を受け取れるようにカンマ演算子が使える。
カンマ演算子は使われている場所によって、どんなオペランドを取るかが変わってくる。
その為ドキュメントではカンマ演算子だけ「さまざまな利用法」という曖昧な表現になっている。
そしてカンマ演算子で繋げられたオペランドたちは、式にはならない。
・for文のカッコ内だったらfor_expr
・echo構文の後だったらecho_expr_list
・関数定義の引数だったらparameter_list
・関数呼び出しの引数だったらfunction_call_parameter_list
というように式(expr)と互換性のない形になっていて、使える場所が限られている。
これはカッコでカンマ演算子の優先度を変えられない事を意味する。
つまりPHPのカンマ演算子は、演算子と言いつつもセパレータ以上の意味を持たない。
個人的にカンマは演算子ではなく構文だと思ってる。
グローバルに使える演算子ではなく、局所的に使える構文。
仕様やドキュメントはどこにあるの?
多分ない。
PHPのメーリングリストとかを漁ればあるかもしれないけど、
多分PHPのソースの Zend/zend_language_parser.y を読んだ方が速い。
結論
バグではなく仕様です。(本当に)
訳の解らない挙動をしている訳ではなく、仕様がちょっと変なだけ。