Recipe 7.10. Cloning Objects


7.10.1. Problem

You want to copy an object.

7.10.2. Solution

Copy objects by reference using =:

$rasmus = $zeev;

Copy objects by value using clone:

$rasmus = clone $zeev;

7.10.3. Discussion

PHP 5 copies objects by reference instead of value. When you assign an existing object to a new variable, that new variable is just another name for the existing object. Accessing the object by the old or new name produces the same results.

To create an independent instance of a value with the same contents, otherwise known as copying by value, use the clone keyword. Otherwise, the second object is simply a reference to the first.

This cloning process copies every property in the first object to the second. This includes properties holding objects, so the cloned object may end up sharing object references with the original.

This is frequently not the desired behavior. For example, consider the aggregated version of Person that holds an Address object in Example 7-26.

Using an aggregated class

class Address {   protected $city;   protected $country;   public function setCity($city) { $this->city = $city; }   public function getCity() { return $this->city; }   public function setCountry($country) { $this->country = $country; }   public function getCountry() { return $this-> country;} } class Person {  protected $name;  protected $address;  public function __construct() { $this->address = new Address; }  public function setName($name) { $this->name = $name; }  public function getName() { return $this->name; }  public function __call($method, $arguments) {   if (method_exists($this->address, $method)) {    return call_user_func_array( array($this->address, $method), $arguments);   }  } }

An aggregated class is one that embeds another class inside in a way that makes it easy to access both the original and embedded classes. The key point to remember is that the $address property holds an Address object.

With this class, Example 7-27 shows what happens when you clone an object.

Cloning an aggregated class

$rasmus = new Person; $rasmus->setName('Rasmus Lerdorf'); $rasmus->setCity('Sunnyvale'); $zeev = clone $rasmus; $zeev->setName('Zeev Suraski'); $zeev->setCity('Tel Aviv'); print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.'; print $zeev->getName() . ' lives in ' . $zeev->getCity() . '.'; Rasmus Lerdorf lives in Tel Aviv. Zeev Suraski lives in Tel Aviv.

Interesting. Calling setName( ) worked correctly because the $name property is a string, so it's copied by value. However, since $address is an object, it's copied by reference, so getCity( ) doesn't produce the correct results, and you end up relocating Rasmus to Tel Aviv.

This type of object cloning is known as a shallow clone or a shallow copy. In contrast, a "deep clone" occurs when all objects involved are cloned. This is PHP 4's cloning method.

Control how PHP 5 clones an object by implementing a __clone( ) method in your class. When this method exists, PHP allows __clone( ) to override its default behavior, as shown in Example 7-28.

Properly implementing cloning in aggregated classes

class Person {  // ... everything from before  public function __clone() {   $this->address = clone $this->address;  } }

Inside of __clone( ), you're automatically presented with a shallow copy of the variable, stored in $this, the object that PHP provides when __clone( ) does not exist.

Since PHP has already copied all the properties, you only need to overwrite the ones you dislike. Here, $name is okay, but $address needs to be explicitly cloned.

Now the clone behaves correctly, as shown in Example 7-29.

Cloning an aggregated class

$rasmus = new Person; $rasmus->setName('Rasmus Lerdorf'); $rasmus->setCity('Sunnyvale'); $zeev = clone $rasmus; $zeev->setName('Zeev Suraski'); $zeev->setCity('Tel Aviv'); print $rasmus->getName() . ' lives in ' . $rasmus->getCity() . '.'; print $zeev->getName() . ' lives in ' . $zeev->getCity() . '.'; Rasmus Lerdorf lives in Sunnyvale. Zeev Suraski lives in Tel Aviv.

Using the clone operator on objects stored in properties causes PHP to check whether any of those objects contain a __clone( ) method. If one exists, PHP calls it. This repeats for any objects that are nested even further.

This process correctly clones the entire object and demonstrates why it's called a deep copy.

7.10.4. See Also

Recipe 7.9 for more on assigning objects by reference.




PHP Cookbook, 2nd Edition
PHP Cookbook: Solutions and Examples for PHP Programmers
ISBN: 0596101015
EAN: 2147483647
Year: 2006
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net