When we clone an object, that contains other objects is important to keep in mind that the objects stored in the properties of the object being cloned will stay as references to the original object, by other words will not be cloned, thus any change in this objects will have a direct reflex on the original object. This type of Clone is called a Shallow Object Copy.
So we can control what happens when an object is cloned by implementing the method __clone()
and inside of it we will ensure that all properties containing another object will contain also a clone of that object. When we Clone with this approach we do a Deep Object Copy.
Clone with Shallow Object Copy
Let’s see a simple example of clone an object Person that contains another object Address.
<?php
class Address
{
private $street;
public function setStreet($street)
{
$this->street = $street;
}
public function getStreet()
{
return $this->street;
}
}
class Person
{
private $name;
private $address;
public function __construct(Address $address)
{
$this->address = $address;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setStreet($street)
{
$this->address->setStreet($street);
}
public function getStreet()
{
return $this->address->getStreet();
}
}
$person = new Person(new Address);
$person->setName('Exadra37');
$person->setStreet('London Avenue');
// Exadra37 street is London Avenue
echo $person->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
// Performing a Clone with a Shallow Copy
$anotherPerson = clone $person;
$anotherPerson->setName('John Doe');
$anotherPerson->setStreet('Edinburgh Avenue');
// John Doe street is Edinburgh Avenue
echo $anotherPerson->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
echo PHP_EOL.'------- Change Another Person Street -------'.PHP_EOL;
// This will set the street in both $person and $anotherPerson
// because properties holding objects are not cloned and Php will
// maintain the same reference to the object Address in $person and $anotherPerson
// resulting in both persons to have now the same street,
// despite our intention in update only the address for $anotherPerson
$anotherPerson->setStreet('Liverpool Avenue');
// At a first glance you may expect to echo:
// - Exadra37 street is London Avenue
// But instead you get:
// - Exadra37 street is Liverpool Avenue
//
// An Liverpool Avenue is the same address of $anotherPerson
echo $person->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
// John Does street is Liverpool Avenue
echo $anotherPerson->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
echo PHP_EOL.'>>>>> Clone with Shallow Copy will trip you with unexpected results... <<<<<'.PHP_EOL;
So the end result is not we may expect at a first glance… don’t worry, because any Php Developer can be tripped by this one, even when we already are aware of this, but is easy to forget when not used often.
Clone with Deep Object Copy
Let’s fix the previous code, using Shallow Object Copy, by adding a __clone()
method to it and ensure that we perform a Deep Object Copy.
<?php
class Address
{
private $street;
public function setStreet($street)
{
$this->street = $street;
}
public function getStreet()
{
return $this->street;
}
}
class Person
{
private $name;
private $address;
public function __construct(Address $address)
{
$this->address = $address;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setStreet($street)
{
$this->address->setStreet($street);
}
public function getStreet()
{
return $this->address->getStreet();
}
public function __clone()
{
$this->address = clone $this->address;
}
}
$person = new Person(new Address);
$person->setName('Exadra37');
$person->setStreet('London Avenue');
// Exadra37 street is London Avenue
echo $person->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
// Performing a Clone with a Deep Copy, once $person object has the __clone() $method
$anotherPerson = clone $person;
$anotherPerson->setName('John Doe');
$anotherPerson->setStreet('Edinburgh Avenue');
// John Doe street is Edinburgh Avenue
echo $anotherPerson->getName() . ' street is ' . $anotherPerson->getStreet() . PHP_EOL;
echo PHP_EOL.'------- Change Another Person Street -------'.PHP_EOL;
// Now that we have a Deep Clone of $anotherPerson we can set the street name without affecting $person object
// because PHP is not holding a reference to the original object
$anotherPerson->setStreet('Liverpool Avenue');
// Exadra37 street is London Avenue
echo $person->getName() . ' street is ' . $person->getStreet() . PHP_EOL;
// John Does street is Liverpool Avenue
echo $anotherPerson->getName() . ' street is ' . $anotherPerson->getStreet() . PHP_EOL;
echo PHP_EOL.'>>>>> Deep Copy Work as Expected instead of Tripping you , like Shallow Copy does... <<<<<'.PHP_EOL;
That’s it… now we have the expected result.
Compare the code and read carefully the comments on it to understand exactly what is going on.
If you still have any doubt or want to point out something, please drop a line in the comments.
Disclaimer: What I expressed here is only in my behalf and doesn't represent the company I work for, or any previous one, neither my family, friends, colleagues or anyone else unless I explicitly say so.
Comments
Disqus is disabled on my Blog because I don't agree with their policies.