Какой самый быстрый способ сравнить два объекта в PHP?

Допустим, у меня есть объект - в данном случае объект User - и я хотел бы иметь возможность отслеживать изменения с помощью отдельного класса. Объект User не должен каким-либо образом изменять свое поведение, чтобы это произошло.

Поэтому мой отдельный класс создает его «чистую» копию, сохраняет его где-то локально, а затем позже может сравнить объект User с исходной версией, чтобы увидеть, изменилось ли что-нибудь в течение срока его службы.

Есть ли функция, шаблон или что-нибудь, что может быстро сравнить две версии объекта User?

Опция 1 Может быть, я мог бы сериализовать каждую версию и напрямую сравнить или хешировать и сравнивать?

Вариант 2 Может быть, я должен просто создать ReflectionClass, пройти через каждое из свойств класса и посмотреть, имеют ли две версии одинаковые значения свойств?

Вариант 3 Может быть, есть простая нативная функция вродеobjects_are_equal($object1,$object2);?

Какой самый быстрый способ сделать это?

 johnnietheblack26 мар. 2012 г., 07:18
Ну, я знаю, что мог бы сериализовать оба объекта и сравнить хэши или что-то в этом роде ... это не МОЖЕТ ли я, это больше о том, как мне сделать это быстро? Я задам вопрос, чтобы отразить этот дух ...

Ответы на вопрос(4)

$reflection_1 = new \ReflectionClass($object_1);
$reflection_2 = new \ReflectionClass($object_2);

$props_1 = $reflection_1->getProperties();

foreach ($props_1 as $prop_1) {

    $prop_1->setAccessible(true);
    $value_1 = $prop_1->getValue($object_1);

    $prop_2 = $reflection_2->getProperty($prop_1->getName());

    $prop_2->setAccessible(true);
    $value_2 = $prop_2->getValue($object_2);

    if ($value_1 === $value_2) {
        // ...
    } else {
        // ...
    }
}


Обратите внимание, что если$object_1 имеет свойства, которые$object_2 не имеет, приведенный выше код будет выдавать ошибки при попытке доступа к ним.

В таком случае вам нужно будет сначала пересечь$reflection_1->getProperties() с участием$reflection_2->getProperties().

который занимается только сравнением объектов:

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

Также хорошо читать:

http://www.tuxradar.com/practicalphp/6/12/0

Я бы сказал, что вы должны использовать метод «клон» при копировании вашего объекта, а затем сравнить его, как сказано в статье php.net.

Также быстрым способом было бы объявление статического метода в классе, который просто сравнивает соответствующие «свойства», например, скажем, «имя пользователя», «пароль», «cookie», ...

    class User
    {

    public $username;
    public $password;
    public $cookie;

    # other code

    public static function compare(&$obj1, &$obj2)
    {
    if ($obj1->username != $obj2->username)
        return 0
    if ($obj1->password != $obj2->password)
        return 0
    if ($obj1->cookie != $obj2->cookie)
        return 0
    return 1
    }


    };       
 johnnietheblack26 мар. 2012 г., 07:48
Правда! Я просто не хочу, чтобы мой класс User беспокоился о том, чтобы сравнивать себя с чем-то еще, поскольку он не касается пользователя. Это касается только службы, которая заботится о том, изменился ли вообще пользователь ... вы знаете?
 user94596726 мар. 2012 г., 07:37
Вот почему существуют статические методы для взаимодействия между объектами одного и того же класса :). С помощью этого метода вы также можете быстро определить, какое «свойство» изменилось, просто вернув ассоциацию. массив как $ result ["username" => "same", "password" => "same", "cookie" => "different"]
 johnnietheblack26 мар. 2012 г., 07:30
Обновление очень хорошее, как я уже сказал в вопросе ... Я бы хотел, чтобы пользователю не пришлось НИЧЕГО менять, чтобы сделать это сравнение возможным ...
 user94596726 мар. 2012 г., 07:54
Да, я понимаю, я однажды сделал то же самое с регистрацией времени для сотрудников. Там я использовал этот метод для сравнения $ time-свойства, а также кэшировал его один раз в день через файл в формате json. Таким образом, вам не нужно было каждый раз запрашивать сотрудника, и вы могли просто указать, сколько времени он работал.
Решение Вопроса

защищенным и приватным) свойствам объекта без его изменения - черезотражение, Вы могли бы, если бы захотели,клон перед изменением объекта, а затем сравните их, используя функции отражения, чтобы просмотреть различные свойства одного из двух объектов и сравнить значения. Это эффективно скажет вам, какие свойства изменились.

Если вам нужно посмотреть, изменились ли объекты, используйте операторы == или ===. Посмотрите насравнивая объекты раздел документации PHP.

Тем не менее, не будет ли проще отслеживать, что вы изменили, когда вы меняете их?

 Francois Deschenes26 мар. 2012 г., 07:25
@johnnietheblack - вы можете использовать события Doctrine, но для этого потребуется немного изменить ваши объекты. Если вас интересуют события Доктрины, взгляните наstackoverflow.com/questions/2059399/..., Это для Doctrine 1.2, но принцип в новой версии тот же.
 johnnietheblack26 мар. 2012 г., 07:28
Еще раз спасибо - мне бы не хотелось настраивать класс User каким-либо образом, чтобы я мог удалить «слушателя», и пользователь не узнает разницу. Кроме того, я хотел бы узнать, как сделать это без Доктрины ... частично для удовольствия, и частично, если мне когда-нибудь понадобится узнать, как это делается иначе
 johnnietheblack26 мар. 2012 г., 07:22
Спасибо за ответ :) Могу, но я исследую Doctrine2, и похоже, что они создают гигантский граф объектов, и после вызова flush () они проверяют, что изменилось, и обновляют все одним махом .... это то, что я пытаюсь выяснить.
 Francois Deschenes26 мар. 2012 г., 07:35
@johnnietheblack - В теории это правильно. Я никогда не пробовал это с Doctrine, но с обычными классами (не управляемыми Doctrine), что было бы действительно так.
 johnnietheblack26 мар. 2012 г., 07:32
Сладкое спасибо :) Итак, если я создал клон User и изменил адрес электронной почты одного из них, то $ user! = $ UserClone, да?

Простой пример сравнения класса dto с помощью отражения

class InvoiceDetails
{
    /** @var string */
    public $type;

    /** @var string */
    public $contractor;

    /** @var string */
    public $invoiceNumber;

    /** @var \DateTimeInterface|null */
    public $saleDate;

    /** @var \DateTimeInterface|null */
    public $issueDate;

    /** @var \DateTimeInterface|null */
    public $dueDate;

    /** @var string */
    public $payment;

    /** @var string */
    public $status;


    public function isEqual(InvoiceDetails $details): bool
    {
        $reflection = new \ReflectionClass(self::class);

        /** @var \ReflectionProperty $property */
        foreach ($reflection->getProperties() as $property) {
            $name = $property->getName();
            if ($this->$name !== $details->$name) {
                return false;
            }
        }

        return true;
    }

}

Ваш ответ на вопрос