Оператор clone в php: как работает?

Здравствуйте!

Сейчас изучаю php по одной книге ( PHP Cookbook David Sclar, Trachtenberg).
Там в 7.10 встретил такой код:

class Person {
  // ... everything from before
  public function __clone() {
    //$this->address --- <b>это объект</b>
    $this->address = clone $this->address;
  }
}


Там сказано, что при вызове оператора clone для экземпляра объекта Person, автоматически будет вызвано
__clone(). Но то что находится внутри __clone ввело меня замешательство :
$this->address = clone $this->address;
Что то я не понял, мы клонируем объект но какого черта тут $this в обеих частях выражения?

Буду рад вашим ответам
  • Вопрос задан
  • 12327 просмотров
Пригласить эксперта
Ответы на вопрос 4
ivankomolin
@ivankomolin
Это сделано для того, чтобы при клонировании объекта Person, одно из его свойств(которое является объектом) тоже было клонировано.

Пример:
$person1 = new Person;
//Например $person1->address это объект со свойством id = 1
$person2 = clone $person1;
//Устанавливаем свойство id для объекта $person2->address
$person2->address->id = 2;
echo $person1->address->id; //выведет 1
echo $person2->address->id; //выведет 2


Теперь представим что public function __clone() {} внутри класса Person нет
$person1 = new Person;
//Например $person1->address это объект со свойством id = 1
$person2 = clone $person1;
//Устанавливаем свойство id для объекта $person2->address
$person2->address->id = 2;
echo $person1->address->id; //выведет 2
echo $person2->address->id; //выведет 2

Т.к. объект Address не был клонирован, мы обращаемся к одному и тому же объекту Address
Ответ написан
Комментировать
AMar4enko
@AMar4enko
Если я правильно понял причину вашего замешательства - $this внутри clone это уже экземпляр-копия.
Т.е. при вызове $objDup = clone $obj;
объект $obj копируется в новый объект $objDup, после чего вызывается ___clone, $this в котором ссылается уже на $objDup.
Т.е. __clone это не обработчик копирования, а хук, который вызывается после копирования в для объекта-копии.
Ну и напоследок: php.net/manual/ru/language.oop5.cloning.php
Ответ написан
@Arik
Да, очень кривой пример или вы не до конца все показали. По сути свойство address хранит объект (а точнее ссылку на объект) и если просто сделать клон объекта person, то и клон будет ссылаться на объект адрес и если один объект person что-то сделает со своим объектом ($this->address) изменит часть данных, то и клон тоже обновит свой ($this->address) - вернее они ссылаются на один объект.
Другими словами с какой-то там версии PHP на объекты лишь ссылаются и объект удаляется из памяти, когда на него никто больше не ссылается.
Пример: сим ссылки в ФС (ярлыки), на один файл можно много ссылок сделать, но если хоть по одной ссылке кто-то изменит данные файла, то и все сим ссылки обновят контент. Поэтому если вам нужно будет изменять файл, то вы не делаете ссылку, а копируете. Тут тоже самое, если адрес могут изменить, то лучше сделать и клон объекта с адресом

Живой пример если не сделать клон объекта адреса:
$person = new Person();

$person->address->a = 1;

$personClone = clone $person;

$person->address->a = 2; // обновляем через первый объект person

echo $personClone->address->a; // 2 - клон тоже обновил, так как они ссылают на один объект
Ответ написан
Комментировать
Если разобраться, то все просто.

Метод __clone, как уже писали ранее, вызывается ПОСЛЕ клонирования объекта. До его вызова php создает "поверхностную" копию объекта. Это значит, что если в одном из свойств клонируемого объекта хранится другой объект, то php копирует в новый объект его ссылку. После этой операции $original->address и $clone->address указывают на один и тот же объект. (пример кода ниже).

Если нам нужно клонировать какое-то свойство-объект, то мы можем сделать это в методе __clone. Мне привычнее воспринимать его как коллбэк. В этом методе $this - это клон (новый объект).

Теперь вспомним то, что php при присваивании выполняет сначала ПРАВУЮ часть операции, а затем левую.
В данном случае это значит: "Эй, интерпретатор, возьми и клонируй то, что лежит у меня в поле adress, потом возьми ссылку на этот объект, и запиши мне в поле address"

class Person {
  public function __clone() {
    $this->address = clone $this->address;
  }
}

$original = new Person();
$clone = clone $original;
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы