jump to navigation

Object Cloning in PHP October 12, 2010

Posted by Tournas Dimitrios in PHP.
trackback

One of the biggest drawbacks to PHP 4’s object-oriented capabilities was its treatment of objects as just another datatype, which impeded the use of many common OOP methodologies, such as design patterns. Such methodologies depend on the ability to pass objects to other class methods as references, rather than as values. Thankfully, this matter has been resolved with PHP 5, and now all objects are treated by default as references. However, because all objects are treated as references rather than as values, it is now more difficult to copy an object. If you try to copy a referenced object, it will simply point back to the addressing location of the original object. To remedy the problems with copying, PHP offers an explicit means for cloning an object.To illustrate how PHP only makes a reference to the original object using the = operator look at the following example.
.


.
class foo {
    public $bar;
}

$foo = new foo();
$foo->bar = 'baz';

$foo2 = $foo;
$foo2->bar = 'bat';

echo $foo->bar;
echo $foo2->bar;

Both echo lines at the end of the example will echo “bat” and not “baz” and “bat”, because $foo2 is a reference to $foo and accessing properties and calling methods for $foo2 is actually referring to those properties and methods of $foo.

Using clone  statement to copy an object  :

Using the same example, but using clone instead to copy the object will result in a copy of the original object. Changing properties and calling methods will do so to the copied object and not the original.The only difference between the above example and this one is “=” becomes “clone” .

class foo {
    public $bar;
}

$foo = new foo();
$foo->bar = 'baz';

$foo2 = clone $foo;
$foo2->bar = 'bat';

echo $foo->bar . "\n";
echo $foo2->bar . "\n";

The second example outputs “baz” and then “bat”.

Shallow copy :

Cloning an object creates a “shallow” copy of the original object. What this means is that any properties that are references to other variables will remain references to those variables and not copies themselves.

The __clone method :

If the object contains a __clone method it will be called when the object is cloned. It must be a publicly accessible function and cannot be called directly otherwise PHP will end with a fatal error. You wouldn’t actualy echo output when cloning, but if the object was cloned in the example below, it would echo out “i’ve been cloned”.


.
class foo {
    public $bar;
    public function __clone() {
        echo "i've been cloned";
    }
}
/*
If you attempt to call the __clone() method yourself, the following error will occur:
*/
Fatal error: Cannot call __clone() method on objects - use 'clone $obj' instead in ...

When and how to use __clone :
Let me repeat these two lines , as its the key to understand the whole concept (object cloning) : Cloning an object creates a “shallow” copy of the original object. What this means is that any properties that are references to other variables will remain references to those variables and not copies themselves . An example will make the whole concept understandable .

<?php

class GamePoints {
public $totoalPoints ;
function __construct( $points ) {
$this->totalPoints = $points ;
}
}

class LoggedIn {

// Check if user is Logged in and return TRUE  or FALSE

}

class User {
public $name ;
private $id ;
public $gamePoints ;
function __construct( $name ,   GamePoints $gamePoints ) {
$this->name = $name ;
$this->gamePoints = $gamePoints ;
}
public function setId( $id ) {
$this->id = $id ;
}
public function  addPoints() {
$this->gamePoints->totalPoints += 100 ;

}
function __clone() {
$this->id = 'member' ;

}
}
$player1 = new User( "John", new GamePoints(500 ) ) ;
if(new LoggedIn($player1->name))  $player1->setId('admin' ) ;

$player2 = clone $player1  ;
echo $player1->gamePoints->totalPoints . '\n' ; //  500
echo $player2->gamePoints->totalPoints  . '\n' ; //  500
$player1->addPoints() ;
echo $player1->gamePoints->totalPoints . '\n' ; //  600
echo $player2->gamePoints->totalPoints  . '\n' ; //  600

Solution : All references inside a “hard copied” object should also be “hard copied” .

<?php

class GamePoints {
public $totoalPoints ;
function __construct( $points ) {
$this->totalPoints = $points ;
}
}

class LoggedIn {

// Check if user is Logged in and return TRUE  or FALSE

}

class User {
public $name ;
private $id ;
public $gamePoints ;
function __construct( $name ,   GamePoints $gamePoints ) {
$this->name = $name ;
$this->gamePoints = $gamePoints ;
}
public function setId( $id ) {
$this->id = $id ;
}
public function  addPoints() {
$this->gamePoints->totalPoints += 100 ;

}
function __clone() {
$this->id = 'member' ;

// Hard copy the referenced  object
$this->gamePoints = new GamePoints($this->gamePoints->totalPoints)  ;

}
}
$player1 = new User( "John", new GamePoints(500 ) ) ;
if(new LoggedIn($player1->name))  $player1->setId('admin' ) ;

$player2 = clone $player1  ;
echo $player1->gamePoints->totalPoints . '\n' ; //  500
echo $player2->gamePoints->totalPoints  . '\n' ; //  500
$player1->addPoints() ;
echo $player1->gamePoints->totalPoints . '\n' ; //  600
echo $player2->gamePoints->totalPoints  . '\n' ; //  500
Advertisements

Comments»

No comments yet — be the first.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s