Копирование объектов с помощью метода clone ()

В РНР 4 копирование объекта выполнялось очень просто— достаточно было присвоить значение одной объектной переменной другой,

class СоруМе {}

$first = new СоруМе(); $second = $first;

//В РНР 4 переменные $second and $first ссылаются на 2 разных объекта

// Начиная с РНР 5 переменные $second and $first ссылаются на один объект

Но эта простота часто служила источником множества ошибок, поскольку ко­пии объекта случайно «размножались» при присвоении значений переменным, вы­зове методов и возврате объектов. Еще больше ухудшало ситуацию то, что не суще­ствовало способа протестировать две переменные, чтобы понять, относятся ли они к одному и тому же объекту. С помощью оператора эквивалентности (==) можно было проверить идентичность значений всех полей у двух объектов либо являются ли обе переменные объектами (===). Но в результате этих проверок нельзя было по­нять, указывают ли обе переменные на один и тот же объект.

В РНР 5 объекты всегда присваиваются и передаются по ссылке. Это означает, что если предыдущий пример запустить в РНР 5, переменные $f irst и $second бу­дут содержать ссылки на один и тот же объект, а не на две его копии. И хотя в большинстве случаев при работе с объектами это именно то, что нам нужно, будут ситуации, когда нам понадобится получить копию объекта, а не ссылку на объект.

В РНР 5 для этой цели предусмотрено ключевое слово clone. Оно применяется к экземпляру объекта и создает дополнительную копию.

class СоруМе {}

$first = new СоруМе О;

$second = clone $first;

//В PHP 5 и более поздних версиях переменные $second and $first

// ссылаются на два разных объекта

И здесь вопросы, касающиеся копирования объектов, только начинаются. Рас­смотрим класс Person, который мы создали в предыдущем разделе. Стандартная копия объекта Person содержит идентификатор (свойство $id), который при реа­лизации полноценного приложения будет использоваться для нахождения нужной строки в базе данных. Если мы разрешим копировать это свойство, то получим два различных объекта, ссылающихся на один и тот же источник данных, причем, ве­роятно, не тот, который мы хотели, когда создавали копию. Изменение в одном объекте повлияет на другой и наоборот.

К счастью, мы можем контролировать то, что копируется при использовании пе­ред объектом ключевого слова clone. Для этого следует реализовать специальный метод_clone () (обратите внимание на два символа подчеркивания, с которых на­чинаются имена всех встроенных методов). Метод clone () вызывается автомати­чески, когда для копирования объекта используется ключевое слово clone.

При реализации метода_clone () важно понимать контекст, в котором рабо­тает данный метод. Метод clone () работает в контексте скопированного объек­та, а не исходного. Давайте добавим метод  clone () к одной из версий нашего класса Person.

class Person {

private $name;

private $age;

private $id;

function __ construct( $name, $age ) {

$this->name = $name;

$this->age = $age;

}

function setld( $id ) {

$this->id = $id;

}

function _ clone() {

$this->id = 0;

Когда оператор clone вызывается для объекта типа Person, создается его новая плоская (shallow) копия, и для нее вызывается метод___ clone (). Это означает, что все изменения значений свойств, выполняемые в методе_clone , отразятся только на новой копии объекта. Старые значения, полученные из исходного объек­та, будут затерты. В данном случае мы гарантируем, что свойство $id скопирован­ного объекта устанавливается равным нулю.

$person = new

Person( «Петр», 44 );

$person->setId( 343 );

$person2 = clone $person;

// $person2 :

// паше: «Петр»

// age: 44

// id: 0.

Выполнение плоской копии гарантирует, что значения элементарных свойств бу­дут скопированы из старого объекта в новый. Однако свойства-объекты будут скопи­рованы по ссылке. Возможно, это не совсем то, что вы хотите, когда клонируете объ­ект. Предположим, мы добавим к объекту Person свойство-объект Account. В этом объекте хранятся данные о состоянии счета, которые мы тоже хотим скопировать в клонированный объект. Однако при этом мы не хотим, чтобы в обоих объектах Person сохранялись ссылки на один и тот же счет.

class Account {

public $balance;

function __ construct( $balance ) {

$this->balance = $balance;

class Person {

private $name; private $age;

private $id;

public $account;

function _ construct( $name, $age. Account $account ) {

$this->name = $name;

$this->age = $age; $this->account = $account;

}

function setld( $id ) {

$this->id = $id;

}

function _ clone() {

$this->id = 0;

$person = new Person{ «Иван», 44, new Account( 200 ) );

$person->setId( 343 );

$person2 = clone $person;

// Добавим $person немного денег

$person->9Gcount->balance += 1Q;

// Это изменение увидит и $person2

print $person2->account->balance;

Объектная переменная $person содержит ссылку на объект типа Account, ко­торый мы сделали общедоступным ради компактности (как вы знаете, мы обычно ограничиваем доступ к свойству, создавая в случае необходимости метод Доступа). Когда создается клон, он содержит ссылку на тот же самый объект Account, на ко­торый ссылается и $person. Мы демонстрируем это, добавляя немного денег к балансу объекта Account переменной $person, а затем распечатывая ее значение с помощью переменной $person2.

Если мы не хотим, чтобы после выполнения операции клонирования в новом объекте осталась ссылка на старое свойство-объект, последнее нужно клонировать явно в методе  clone ():

function _ clone() {

$this->id = 0;

$this->account = clone

$this->account;

}

Добавить комментарий