All Articles

PHPにおけるオブジェクトの比較

業務中にとあるクラスのオブジェクトを比較している箇所で不思議な挙動をしていたので、改めてドキュメントを読み直した。

オブジェクトの比較

PHPにおける、オブジェクト比較の定義として以下が挙げられる。

  • 組み込みのクラスには独自の比較基準が定義されている
  • 組み込みクラス以外の比較は出来ない
  • 比較演算子==では、同じ属性と値を持ち、同じクラスのインスタンスであればtrueとなる
  • 厳密な比較演算子===では、同じクラスの同じインスタンスを参照している場合のみtrueとなる

上記に関しては、以下のドキュメントページから引用した。

組み込みのクラスには独自の比較基準が定義されている

ここではDateTimeを用いる。

<?php

$dt1 = new DateTime();
$dt2 = new DateTime('+1 day');
$dt3 = new DateTime('-1 day');

var_dump($dt1 == $dt2); // false
var_dump($dt1 == $dt3); // false
var_dump($dt1 < $dt2); // true
var_dump($dt1 > $dt3); // true

上記の結果から、直感的な比較が行えていることがわかる。

DateTimeに関してはマイクロ秒を保持しているので、等価を調べる際は注意が必要。

組み込みクラス以外の比較は出来ない

比較そのものが出来ないわけではなく、大小の比較が出来ないといった方が正しい気がする。

ここではDummyというクラスを作成して検証する。

<?php

class Dummy
{
    public $num1;
    public $num2;
    public $str;

    public function __construct(int $num1, int $num2, string $str)
    {
        $this->num1 = $num1;
        $this->num2 = $num2;
        $this->str = $str;
    }
}

$cls1 = new Dummy(10, 200, 'hoge');
$cls2 = new Dummy(11, 10, 'hoge');
$cls3 = new Dummy(10, 200, 'hoge');

var_dump($cls1 == $cls2); // false
var_dump($cls1 == $cls3); // true

// $num1での比較しか行われていない
var_dump($cls1 > $cls2); // false
var_dump($cls1 < $cls2); // true

同じ属性、同じ値であり、同じクラスのインスタンスであれば等価を調べることは可能である。 しかし大小比較は厳密に行われていないので、定義したい場合はメソッドを作成する必要がある。

比較演算子のページにある配列の比較例を見るに、 プロパティを定義されている順に比較し、大小関係が判定できた時点で以降の値は見ていないのだと思う。

比較演算子==によるオブジェクトの比較

ここでも先程のDummyを利用して検証する。

$cls1 = new Dummy(10, 200, 'hoge');
$cls2 = new Dummy(10, 200, 'hoge');
$cls3 = clone $cls1;
$cls4 = clone $cls2;
$cls5 = new Dummy(0, 10, 'fuga');

var_dump($cls1 == $cls2); // true
var_dump($cls1 == $cls3); // true
var_dump($cls1 == $cls4); // true
var_dump($cls1 == $cls5); // false

先述している通り、同一の属性・値を持ち、同じクラスのインスタンスであればtrueとなる。 $cls5は全ての値を変更しているが、1つでも異なる値になっていればfalseとなる。

厳密な比較演算子===によるオブジェクトの比較

同様にDummyを利用する。

$cls1 = new Dummy(10, 200, 'hoge');
$cls2 = new Dummy(10, 200, 'hoge');
$cls3 = clone $cls1;
$cls4 = $cls1;
$cls5 = clone $cls4;

var_dump($cls1 === $cls2); // false
var_dump($cls1 === $cls3); // false
var_dump($cls1 === $cls4); // true
var_dump($cls1 === $cls5); // false

比較演算子==trueとなっていた比較も厳密な比較演算子の場合はfalseとなり、 同一の参照を持つ場合でなければtrueとならない。

おまけ:DateIntervalの比較

冒頭に不思議な挙動をしていたと書いたが、正体はDateInterval

次のようなコードの比較結果を確認してみた。

$interval1 = new DateInterval('P10D');
$interval2 = new DateInterval('P20D');

var_dump($interval1 == $interval2);
var_dump($interval1 < $interval2);
var_dump($interval1 > $interval2);

このときvar_dump()は1つ目がtrueとなり、以降はfalseと表示される。 先程書いたとおりにプロパティを順に比較していれば、$interval2の方が大きいと判定されて2つ目のvar_dump()のみtrueとなるはず。

ネタバラシをするとバグであり、こちらで報告されている。推測としては、組み込みクラスでないためオブジェクト同士の比較をサポートしていないと思われる。

日付に関する比較をする場合は必ずDateTimeを使って行わないといけないことが学べた。

Reference