PHPのソートとかarray_uniqueの挙動
少し前にPHP 5.2.9でarray_uniqueの仕様が変更されてアレだって話が話題になっていたが、今のプロジェクトでもそれが問題に上がった。悩ましい。
array_unique関数がPHP5.2.9から後方互換性を失いました - hnwの日記
http://d.hatena.ne.jp/hnw/20090228
上記のエントリにこれ以上ないぐらい分かりやすいサンプルがあるので、PHPで飯食っててこの話題を知らない人は今すぐ見に行くように。
一言で言うとデータが欠落するバグを生みかねない仕様変更なので要注意。
マニュアルにもあるとおりarray_uniqueはまず配列をソートしてから重複を捨てるような処理をするんだけど、そのソートに使用する比較基準が5.2.8まではSORT_STRINGだったのに5.2.9からはSORT_REGULARに変更になった。
そうなると文字列表現は異なっても数値として解釈できる文字列の場合は数値として比較されてしまうので期待と異なる結果になる恐れがあるわけだ。"0x0a"と"1e1"とかね(共に10)。
ところで、他のソート関数はどうなのかというと、どうやらPHP4の頃からデフォルトはSORT_REGULARだったようだ。
- sort()
- rsort()
- ksort()
- krsort()
- asort()
- arsort()
いずれもオプションで第二引数に$sort_flagsを取れるようになっていて、省略時のデフォルトはSORT_REGULARと書かれている(上記以外のソート関数はnatural orderだったりコールバックを取るやつだったりするので関係ない)。
もしかして今回の仕様変更はこれらに合わせるためだったんだろうか……。
確かにそう考えると理解できなくもないけど、やっぱり互換性は保たないとだめだろ。
5.2.8のソースを覗いてみた感じではarray_uniqueはSORT_STRINGになっていたので、5.2.9で従来と同じ動作にするには第二引数にSORT_STRINGを与えればよいことになる。
しかしこの第二引数は5.2.9からなので、5.2.8以前で第二引数をつけて呼び出すとWarningが発生する上に戻り値がNULLになる。バージョン見て使い分けとかめんどくせぇ。
どうでもいいけど
PHPはソート関数多すぎだろ。
個人的にはコールバックを渡して好きなように比較するってのが普通な感じがするなぁ。u*sortだけあればいい感じ。
だいたいソートをしたいときってデータの内容はそれなりに意識するし、それに合わせてcomparatorを作るもんじゃないの? ちゃんぽん相手の時は文字列比較でいいんだよ!
そういう意味ではarray_uniqueでソートの方法を指定できるようになったのはよかったわけだな。ただそのデフォルトがよくなかったと。
……あぁPHPだとコールバックの渡し方ださいし、create_functionとかもってのほかだし、u*sortだけだと嫌だなw