Копирование объектов класса
При присваивании объекта класса другой переменной создается новая ссылка на тот же объект:
<?
class Person
{
public $name;
function __construct($name)
{
$this->name = $name;
}
}
$tom = new Person("Tom");
$bob = $tom;
$bob->name = "Bob";
// Bob
echo $tom->name;
В данном случае после присваивания $bob = $tom;
обе переменных будут указывать на один и тот же объект. Поэтому если мы поменяем свойство $name
у одной переменной, то это измение затронет и другую переменую. Так как они ссылаются на один и тот же объект.
Однако такое поведение может быть нежелательным, если мы хотим, чтобы после копирования переменные представляли независимые друг от друга объекты. И для этого PHP предоставляет оператор clone
:
<?
class Person
{
public $name;
function __construct($name)
{
$this->name = $name;
}
}
$tom = new Person("Tom");
// копируем объект из $tom в переменную $bob
$bob = clone $tom;
$bob->name = "Bob";
// Tom
echo $tom->name;
При применении оператора clone
все свойства, которые представляют примитивные типы и массивы, копируются в новый объект. Однако, что если у нас свойство класса представляет другой класс:
<?
class Company
{
public $name;
function __construct($name)
{
$this->name = $name;
}
}
class Person
{
public $name, $company;
function __construct($name, $company)
{
$this->name = $name;
$this->company = $company;
}
}
$microsoft = new Company("Microsoft");
$tom = new Person("Tom", $microsoft);
// копируем объект из $tom в переменную $bob
$bob = clone $tom;
$bob->name = "Bob";
// изменяем у Боба название компании
$bob->company->name = "Google";
$bob->languages[0] = "french";
// Google - у Тома тоже изменилась компания
echo $tom->company->name;
Здесь в принципе мы сталкиваемся с той же ситуацией, что и в первом примере. Класс Person
содержит свойство, которое представляет класс Company
. При клонировании объекта:
$bob = clone $tom;
Обе переменных $tom
и $bob
будут содержать ссылку на один и тот же объект Company
. Соответственно если через одну переменную изменить свойства этого объекта:
$bob->company->name = "Google";
то изменение затронет и другую переменную:
echo $tom->company->name; // Google
Чтобы выйти из этой ситуации, необходимо в классе определить метод __clone
. Он вызывается при клонировании и может применяться для клонирования вложенных объектов:
<?
class Company
{
public $name;
function __construct($name)
{
$this->name = $name;
}
}
class Person
{
public $name, $company;
function __construct($name, $company)
{
$this->name = $name;
$this->company = $company;
}
function __clone()
{
$this->company = clone $this->company;
}
}
$microsoft = new Company("Microsoft");
$tom = new Person("Tom", $microsoft);
// копируем объект из $tom в переменную $bob
$bob = clone $tom;
$bob->name = "Bob";
// изменяем у Боба название компании
$bob->company->name = "Google";
$bob->languages[0] = "french";
// Microsoft - у Тома НЕ изменилась компания
echo $tom->company->name;