Object Cloning in PHP

Object cloning is the act of making a copy of an object. Cloning in PHP is done by making a shallow copy of the object. This means that internal objects of the cloned object will not be cloned, unless you explicitly instruct the object to clone these internal objects too, by defining the magic method __clone().

In simple words,

Cloning is used to create a genuine copy of an object. Assigning an object to another variable does not create a copy – rather, it creates a reference to the same memory location as the object:

<br />
$o = new stdclass;<br />
$o-&gt;a = 'b';<br />
$o-&gt;b = 'c';</p>
<p>$o2 = $o;<br />
$o2-&gt;a = 'd';</p>
<p>var_dump($o);<br />
var_dump($o2);</p>
<p>$o3 = clone $o;<br />
$o3-&gt;a = 'e';<br />
var_dump($o);<br />
var_dump($o3);<br />

This example code will output the following:

<br />
object(stdClass)#1 (2) {<br />
  [&quot;a&quot;]=&gt;<br />
  string(1) &quot;d&quot;<br />
  [&quot;b&quot;]=&gt;<br />
  string(1) &quot;c&quot;<br />
}<br />
object(stdClass)#1 (2) {<br />
  [&quot;a&quot;]=&gt;<br />
  string(1) &quot;d&quot;<br />
  [&quot;b&quot;]=&gt;<br />
  string(1) &quot;c&quot;<br />
}<br />
object(stdClass)#1 (2) {<br />
  [&quot;a&quot;]=&gt;<br />
  string(1) &quot;d&quot;<br />
  [&quot;b&quot;]=&gt;<br />
  string(1) &quot;c&quot;<br />
}<br />
object(stdClass)#2 (2) {<br />
  [&quot;a&quot;]=&gt;<br />
  string(1) &quot;e&quot;<br />
  [&quot;b&quot;]=&gt;<br />
  string(1) &quot;c&quot;<br />
}<br />

If you don’t utilize the __clone method, the internal objects of the new object will be references to the same objects in memory as the internal objects of the original object that was cloned.

<br />
// in this example the internal member $_internalObject of both objects<br />
// reference the same instance of stdClass in memory.<br />
class CloneableClass<br />
{<br />
    private $_internalObject;</p>
<p>    public function __construct()<br />
    {<br />
        // instantiate the internal member<br />
        $this-&gt;_internalObject = new stdClass();<br />
    }<br />
}</p>
<p>$classA = new CloneableClass();<br />
$classB = clone $classA;</p>
<p>// in this exampe the internal member $_internalObject of both objects<br />
// DON'T reference the same instance of stdClass in memory, but are inividual instances<br />
class CloneableClass<br />
{<br />
    private $_internalObject;</p>
<p>    public function __construct()<br />
    {<br />
        // instantiate the internal member<br />
        $this-&gt;_internalObject = new stdClass();<br />
    }</p>
<p>    // on clone, make a deep copy of this object by cloning internal member;<br />
    public function __clone()<br />
    {<br />
        $this-&gt;_internalObject = clone $this-&gt;_internalObject;<br />
    }<br />
}</p>
<p>$classA = new CloneableClass();<br />
$classB = clone $classA;<br />

Use cases for cloning would for instance be a case where you don’t want outside objects to mess with the internal state of an object.

Let’s say you have a class User with a internal object Address.

<br />
class Address<br />
{<br />
    private $_street;<br />
    private $_streetIndex;<br />
    private $_city;<br />
    // etc...</p>
<p>    public function __construct( $street, $streetIndex, $city /* etc.. */ )<br />
    {<br />
        /* assign to internal values */<br />
    }<br />
}</p>
<p>class User<br />
{<br />
    // will hold instance of Address<br />
    private $_address;</p>
<p>    public function __construct()<br />
    {<br />
        $this-&gt;_address = new Address( 'somestreet', '1', 'somecity' /* etc */ );<br />
    }</p>
<p>    public function getAddress()<br />
    {<br />
        return clone $this-&gt;_address;<br />
    }<br />
}<br />

For arguments sake, let’s say you don’t want outside objects to mess with the internal Address of User objects, but you do want to be able to give them a copy of the Address object. The above example illustrates this. The getAddress method returns a clone of the address object to calling objects. This means that if the calling object alters the Address object, the internal Address of User will not change. If you didn’t give a clone, then the outside object would be able to alter the internal Address of User, because a reference is given by default, not a clone.

An object copy is created by using the clone keyword (which calls the object’s __clone() method if possible).  An object’s __clone() method cannot be called directly.

<br />
$copy_of_object = clone $object;<br />

Creating a copy of an object with fully replicated properties is not always the wanted behavior. If your object holds a reference to another object which it uses and when you replicate the parent object you want to create a new instance of this other object so that the replica has its own separate copy.

<br />
class SubObject<br />
{<br />
    static $instances = 0;<br />
    public $instance;</p>
<p>    public function __construct() {<br />
        $this-&gt;instance = ++self::$instances;<br />
        echo &quot;SubObject construct\n&quot;;<br />
    }</p>
<p>    public function __clone() {<br />
        $this-&gt;instance = ++self::$instances;<br />
        echo &quot;SubObject clone\n&quot;;<br />
    }<br />
}</p>
<p>class MyCloneable<br />
{<br />
    public $object1;<br />
    public $object2;</p>
<p>    function __clone()<br />
    {<br />
        // Force a copy of this-&gt;object, otherwise<br />
        // it will point to same object.<br />
        $this-&gt;object1 = clone $this-&gt;object1;<br />
        echo &quot;MyCloneable clone\n\n&quot;;<br />
    }<br />
}</p>
<p>$obj = new MyCloneable();<br />
echo &quot;new MyCloneable() -- Done with MyCloneable \n\n&quot;;</p>
<p>echo &quot;calling new SubObject() \n&quot;;<br />
$obj-&gt;object1 = new SubObject();<br />
echo &quot;new SubObject() -- Done with SubObject1 \n&quot;;<br />
echo &quot;calling new SubObject() \n&quot;;<br />
$obj-&gt;object2 = new SubObject();<br />
echo &quot;new SubObject() -- Done with SubObject2 \n\n&quot;;</p>
<p>echo &quot;calling clone of MyCloneable object \n&quot;;<br />
$obj2 = clone $obj;</p>
<p>print(&quot;Original Object:\n&quot;);<br />
print_r($obj);</p>
<p>print(&quot;Cloned Object:\n&quot;);<br />
print_r($obj2);<br />

resulting to

<br />
new MyCloneable() -- Done with MyCloneable</p>
<p>calling new SubObject()<br />
SubObject construct<br />
new SubObject() -- Done with SubObject1<br />
calling new SubObject()<br />
SubObject construct<br />
new SubObject() -- Done with SubObject2</p>
<p>calling clone of MyCloneable object<br />
SubObject clone<br />
MyCloneable clone</p>
<p>Original Object:<br />
MyCloneable Object<br />
(<br />
    [object1] =&gt; SubObject Object<br />
        (<br />
            [instance] =&gt; 1<br />
        )</p>
<p>    [object2] =&gt; SubObject Object<br />
        (<br />
            [instance] =&gt; 2<br />
        )<br />
)</p>
<p>Cloned Object:<br />
MyCloneable Object<br />
(<br />
    [object1] =&gt; SubObject Object<br />
        (<br />
            [instance] =&gt; 3<br />
        )</p>
<p>    [object2] =&gt; SubObject Object<br />
        (<br />
            [instance] =&gt; 2<br />
        )<br />
)<br />

'Coz sharing is caring

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.