web-dev-qa-db-ja.com

JavaにあるようなPHPに__equalsメソッドがありますか?

クラスの2つのインスタンスを比較するタイミングを定義するためにPHPで使用できるパターンまたはマジックメソッドはありますか?

たとえば、Javaでequalsメソッドを簡単にオーバーライドし、2つのインスタンスをチェックおよび比較するカスタムの方法を作成できます。

30
Simon

一言で言えば?いいえ。___equals_マジックメソッドはありません。マジックメソッドの完全なリストがあります マニュアル内

できるよ

_$myObject1 == $myObject2
_

同じ属性と値を持ち、同じクラスのインスタンスである場合、それらは等しいと見なされます。

私はこのタイプのメソッドを自分で頻繁に望んでいましたが、より便利なものは、比較演算子<、>、==、===などに対して呼び出される__compare()メソッドだと思います PHP internals wiki に見られるように、PHPの組み込みクラスには既に存在し、 PHPInternals book :-に実装方法の例があります。

compare_objects

_int (*compare)(zval *object1, zval *object2 TSRMLS_DC)
_

2つのオブジェクトを比較します。演算子==、!=、<、>、⇐および> =に使用されます。実装は、同じ比較ハンドラーを共有するオブジェクトa、b、cについて、次の規則に従う必要があります。

私がこれを達成するために使用した1つの方法は、次のようなComparableインターフェースを実装することです。

_interface Comparable
{
    /**
     * @param Comparable $other
     * 
     * @return Int -1, 0 or 1 Depending on result of comparison
     */
    public function compareTo(Comparable $other);
}
_

オブジェクト比較の詳細、およびその他すべてOOP関連するものはここにあります http://www.php.net/manual/en/language.oop5.php

これはPHP 7 で実装できます。

現在、composerを使用してインストールできるこの実装があります。 https://github.com/Fleshgrinder/php-comparable

28
vascowhite

悲しいことではありませんが、非常に簡単に近いものを複製できます。例えば:-

<?php
interface IComparable {
    public function compare(self $subject);
}

class Foo implements IComparable {
    public function compare(self $subject) {
        return $this->__toString() === $subject->__toString();
    }
    public function __toString() {
        return serialize($this);
    }
}

function compare(IComparable $a, IComparable $b) {
    return $a->compare($b);
}

$a = new Foo;
$b = new Foo;

var_dump(compare($a, $b)); //true

$a->name = 'A';
$b->name = 'B';

var_dump(compare($a, $b)); //false

それは特にエレガントではありませんが、あなたの道にあなたを取得する必要があります。

アンソニー。

7

最初に==演算子を使用すれば、ほとんどの場合、特に値オブジェクトについて話している場合は十分です。インスタンスをスカラー値と比較する機能が必要な場合は、必ず__toStringメソッドを提供してください。

<?php

final class ValueObject {

    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function __toString() {
        return (string) $this->value;
    }

}

$a = new ValueObject(0);
$b = new ValueObject(1);

var_dump(
    $a == $b,  // bool(false)
    $a == $a,  // bool(true)
    $b == $b,  // bool(true)
    $a == '0', // bool(true)
    $b == '1'  // bool(true)
);

これには1つの落とし穴があります。文字列以外のスカラー型と比較することはできません(少なくともPHP 5および7)ではありません)インスタンスが目的の値に変換できなかったことを訴えるしたがって、比較する値の型が文字列であることを常に確認する必要があります。

それ以上必要な場合、例えば入力を小文字にするか、特定の精度までフロート値を処理するには、別のアプローチが必要です。これを処理する1つの方法は次のとおりです。

<?php

interface Comparable {

    function compareTo(Comparable $other): int;

}

function class_compare(Comparable $a, Comparable $b): int {
    return $a->compareTo($b);
}

final class C implements Comparable {

    private $value;

    public function __construct(int $value) {
        $this->value = $value;
    }

    public function compareTo(Comparable $other): int {
        assert($this instanceof $other && $other instanceof $this);

        return $this->value <=> $other->value;
    }

}

$c1 = new C(0);
$c2 = new C(0);

var_dump($c1->compareTo($c2)); // int(0)

$c0 = new C(0);
$c1 = new C(1);
$c2 = new C(2);

$actual = [$c2, $c1, $c0];
usort($actual, 'class_compare');

var_dump($actual === [$c0, $c1, $c2]); // bool(true)

PHPにはジェネリックがないため、assertは重要です。これは物事のかなり悲しい状態であり、これを実装するきれいな方法はありません。

私がしがちなのは次のことです。

<?php

final class SomeClass {

    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public static function compare(SomeClass $a, SomeClass $b): int {
        return $a->compareTo($b);
    }

    public function compareTo(SomeClass $other): int {
        return $this->value <=> $other->value;
    }

    public function isEqual($other): bool {
        return $other instanceof $this && $other->value === $this->value;
    }

    public function isIdentical($other): bool {
        return $other instanceof $this && $this instanceof $other && $other->value === $this->value;
    }

}

慣例によりこれらのメソッド名に単に固執するだけで、それらを適切に使用できます。複数のクラス間で望ましいデフォルトの動作を実装する特性を提供することもできます。

4
Fleshgrinder

基本的に、誰もが言うように、これは行います:

$object1 == $object2

タイプとプロパティを比較します。


しかし、この場合に私が行うことは、平等メソッドをパーソナライズしたいときに、平等をアサートしたいクラスにマジックメソッド__toString()を実装することです。

class Car {
  private $name;
  private $model;
   ...
  public function __toString() {
     return $this->name.", ".$this->model;
  }
}

そして、私が比較をしたいとき、私はこれをするだけです:

$car1->toString() === $car2->toString()

そして、2つのインスタンスが同じ属性を持っているかどうかを比較します。

他のオプション(コメントの半分の状態)は、同じクラスの別のインスタンスの等価性をアサートするequalメソッドを実装します。例えば:

class Car {
  private $name;
  private $model;
   ...
  public function equals(Car $anotherCar) {
         if($anotherCar->getName() !== $this->name) {
           return false;
         }

         if($anotherCar->getModel() !== $this->model) {
           return false;
         }
         ...
         return true;
  }
}
1
Tomas Prado

カスタムオブジェクトを比較する場合は、次のように実行できます。

$time1 = new MyTimeClass("09:35:12");
$time2 = new MyTimeClass("09:36:09");

if($time1 > $time2) echo "Time1 is bigger";
else echo "Time2 is bigger";

//result: Time1 is bigger

クラスで最初に見つかったプロパティを比較します。私の場合は、指定された時間の合計秒数を保持するint値です。 $ secondsプロパティを一番上に配置すると、予期しない「Time1の方が大きい」ことになります。

class MyTimeClass {
    public $intValue;
    public $hours;
    public $minutes;
    public $seconds;

    public function __construct($str){
        $array = explode(":",$str);
        $this->hours = $array[0];
        $this->minutes = $array[1];
        $this->seconds = $array[2];
        $this->intValue = ($this->hours * 3600) + ($this->minutes * 60) + $this->seconds;
    }
}
0
Johan Velthuis