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

とりあえず今回の案件では

サードパーティー製のライブラリ使ってるとか、ベンダー製のパッケージシステムを使ってるとか色々あって、がしがし置換していきゃいいって単純な話でもないんだけど、実際のところ誤動作を起こしそうなケースはそうそうないだろうってことで様子を見ることに。
文字列を数値として評価したときと違って、変換不能なら0になるとか頭から解釈できるところまでとかいう動きじゃないみたいなので、まぁ大丈夫なんじゃないかなぁ。方針決める立場じゃないし俺しらね。