SEEDS Creator's Blog

PHP のオブジェクトの比較について

こんにちは kinu です。PHP のオブジェクトを比較したいときに、 はたして比較用の演算子がそのまま使えるのか?と思い調べました。

対象を用意する

まずテスト用にクラスを定義します。

[code] class Test { private $a;

    private $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }

} [/code]

このクラスのインスタンスに対して検証していきたいと思います。

オブジェクトに比較の演算子が使えるのか

以下のコードを実行できるか試してみます。

[code]
$obj1 = new Test(1, 1);
$obj2 = new Test(1, 2);
if ($obj1 == $obj2) {
  echo '同じ'."\n";
}

if ($obj1 < $obj2) {
  echo '小さい'."\n";
}
[/code]

結果

小さい

エラーは出ず、それっぽく動きました。

どのようにして比較されるのか

では実際にどういう判定をしているのか以下のコードでプロパティを数値のみにして検証してみました。 PHPのバージョンは5.6.4です。

[code] function compare($obj1, $obj2) { $result = array();

    if ($obj1 === $obj2) {
        $result[] = 'まったく同じ';
    }

    if ($obj1 == $obj2) {
        $result[] = '同じ';
    }

    if ($obj1  $obj2) {
        $result[] = '大きい';
    }
    return $result;

}

function check($sets, $expect) { foreach ($sets as $set) { $obj1 = new Test($set[0][0], $set[0][1]); if ($set[1][0] == 'clone') { $obj2 = clone $obj1; } else { $obj2 = new Test($set[1][0], $set[1][1]); }

      $result = compare($obj1, $obj2);
      if ($expect != $result) {
          echo '(' . join(',', $set[0]) . '), (' . join(',', $set[1]) . '): ';
          echo join(',', $expect) . ' -> ' . join(',', $result)."\n";
      }
    }

}

//同じになりそうな組み合わせ $same = [ [[1, 1], [1, 1]], [[1, 1], ['clone']], [[1, 2], [2, 1]], [[2, 1], [1, 2]], [[2, 2], [2, 2]], ];

// $obj1 が大きくなりそうな組み合わせ $big = [ [[1, 2], [1, 1]], [[2, 1], [1, 1]], [[2, 2], [1, 1]], [[2, 2], [2, 1]], [[2, 2], [1, 2]], ];

// $obj1 が小さくなるような組み合わせ $small = [ [[1, 1], [1, 2]], [[1, 1], [2, 1]], [[1, 1], [2, 2]], [[1, 2], [2, 2]], [[2, 1], [2, 2]], ];

check($same, array('同じ')); check($big, array('大きい')); check($small, array('小さい')); [/code]

オブジェクトのプロパティの合計値で比較してるのかなと思い上記のようなデータで結果を予想しました。 このプログラムは予想と違っていた場合にその組み合わせと結果を出力します。

結果

[code] (1,2), (2,1): 同じ -> 小さい (2,1), (1,2): 同じ -> 大きい [/code]

どうやら予想とは違うみたいです。組み合わせの数値と結果をみてみると、プロパティの順番が重要そうです。
予想と違った組み合わせでいえば一つ目のプロパティを比較した結果と同じになっています。
その他の組み合わせも一つ目が同じなら次のプロパティを比較して結果を出してると考えるとつじつまが合います。 試しに次の組み合わせもしてみました。

[code] $same = [ [[2, 0], [1, 1]], ]; $small = [ [[2, 1], [1, 3]], ]; check($same, array('同じ')); check($small, array('小さい')); [/code]

結果

[code] (2,0), (1,1): 同じ -> 大きい (2,1), (1,3): 小さい -> 大きい [/code]

最初の予想は完全に間違いであることがわかりました。
またこの結果もひとつめのプロパティの比較と同じになってます。

まとめ

文字列を値にもったプロパティについての検証がないなど中途半端な検証になってしまいましたが、
おそらくオブジェクトを比較すると最初に同値にならなかったプロパティの比較の結果(ない場合は同値)になる
ということで問題ないんじゃないかなと思います。
公式のドキュメントのUser Contributed Notesにもありましたし…。
ということでPHPでオブジェクトを比較したいときは比較用のメソッドを作って使いましょう。

参考

http://php.net/manual/ja/language.oop5.object-comparison.php