PerlプログラマがやってしまうPHPの間違い

いや、まぁ、Perlプログラマというか僕が、なんですけどね。

つーわけで自分用にメモメモ。

Perl感覚でPHP組んでる時によく陥る罠をまとめとく。

ここで言う罠とは文法的には正しいが、処理としては間違ってるものが対象。

文法的にミスってた場合はその場で即エラーになるからいいんだけど効果を勘違いしてる場合はエラーにならないからタチが悪いんだよね。

  • メソッドとメンバ変数の罠

Perlの場合、メソッドを呼び出す際括弧をつけてもつけなくてもいいが、PHPの場合は括弧がついてたらメソッド、ついてなかったらメンバ変数なのでPHPでメソッド呼び出しするときは慎重に。

 # Perl
 $obj->hoge(); # メソッド呼び出し
 $obj->hoge;   # メソッド呼び出し
 
 // PHP
 $obj->hoge(); // メソッド呼び出し
 $obj->hoge;   // メンバ変数
  • デフォ値代入の罠
 # Perl
 my $a = $hoge || $muge || 'default';

コレには参った参った。PHPの場合||で評価されたものは全てboolean型を返す仕様。
だから上記のように書くと$hogeや$mugeにどんな値が入っていようとTRUEが返り、print $aとかやっても表示されるのは「1」だ。

もし、PHPで同じような処理を書きたければ一応、

 // PHP
 ( $a = $hoge ) || ( $a = $muge ) || ( $a = 'default' );

のようにも書けるが見栄えがよろしくないのであまりオススメしない。

Perlでは

 $hash{key_name} = 10;
 print $hash{key_name};   # 10
 print $hash{'key_name'}; # 10

とこのようにハッシュのキー名をシングルクォートで括らなくてもOKだが、PHPは必ずシングルクォートで括らないといけないとマニュアルに書いてある。
だが、括っても括らなくてもちゃんと動作する。なぜか。

 $array[key_name] = 10;
 print $array[key_name];   // 10
 print $array['key_name']; // 10

実はこの挙動には理由があるのだが、その理由を説明する前にPHPにおける定数について説明しよう。
定数を定義するにはdefine()関数を使う。

 define('HOGE','Hellow!!'); // HOGEという定数の定義
 print HOGE; // Hellow!!

さて、感の鋭い方はもうお気づきだろうが、PHPにおける裸の文字列は全て定数と見なされる。
だがその時にその裸の文字列がdefine()で定義されていない場合、デフォルトの挙動として自身と同じ文字列を返すような仕様になっているわけだ。

 // define()せずに裸文字列を表示すると・・・
 print HOGE; // HOGE

と、言うことは$array[key_name]とは結局のところkey_nameという定数を参照していることになる。
つまりもしシングルクォートで括っていなかった場合、どうなるかというと・・・。

 define('key_name','hogehoge');
 $array[key_name] = 10;
 print $array[key_name];   // 10
 print $array['key_name']; // 何も表示されない
 print $array['hogehoge']; // 10

ってなことになってしまうわけなのだ。Perlに慣れている人は特に要注意すべし。

PHPの三項演算子って変じゃね? - 浅倉卓司@blog風味? - ひとりでもグループ

まさにこれ。これは僕も同じ罠に嵌りました。優先順位がブッ飛んでますよね。

これを無理にでも三項演算子を用いて動かしたいのならLispのように括弧だらけになる。

 $per_page = (($per_page < 10) ?        10
           : (($per_page > 50) ?        50
           :                     $per_page))
           ;

うーん。こういう場合はどうするのがいいんでしょ。
if文使うのもなんだかしゃくに障るのでPHP的にswitch使えってことでしょうか?

 // ifを使うケース
 if ( $per_page < 10 ) {
     $per_page = 10;
 }
 elseif ( $per_page > 50 ) {
     $per_page = 50;
 }
 
 // switchを使うケース
 switch (TRUE) {
     case ($per_page < 10): $per_page = 10; break;
     case ($per_page > 50): $per_page = 50; break;
 }

とりあえずざっとこんなもん。
つづく、かも。