What is the purpose of the __clone magic method?

10

In PHP we have the magic method __clone . It is used to define behavior when you clone an object through the keyword clone .

So far so good. But I do not quite understand why having this magic method, since the word clone per se already clones an object.

Example 1:

 $a = new ArrayObject(['nome' => 'Wallace']);

 $b = clone $a;

Another detail I wanted to understand is that I've seen some libraries in PHP use this magic method for operations similar to this below:

Example 2:

  class X {
       public function __construct (Y $y) {
          $this->y = $y;
       }

       public function __clone() {
          $this->y = clone $this->y;
       }
  }

Based on the above comments, I ask:

  • Is there a case where it is really necessary to define a behavior for cloning an object?

  • Why do some libraries use __clone to clone a property corresponding to an instance of an object (as in example 2)?

asked by anonymous 30.06.2016 / 15:47

3 answers

14

Deep clone

Because cloning, in general, involves copying the entire contents of the object deep ( deep ). That is, it also copies the objects referenced within this object. Eventually one may want until every tree of objects is cloned, if it is possible (referenced objects need to be able to be cloned deeply).

As the way to copy these objects can vary greatly, you have to write the code that will do this. This happens a lot when you secure external resources in the class, such as files and GUI objects, but are not limited to these things. Anything that needs complete data independence needs deep cloning.

Shallow clone

If you do not create this method PHP will clone shallow ( shallow ), that is, it will copy only the object, its members by reference will have only the references copied that will point to the same object that its original object pointed. This may be what you want in some cases, but not at all.

So many people like to lose creating classes. Most programmers do not understand all the implications of creating a class. Usually it works because in general the classes do not do anything sophisticated, or even they were not even necessary in fact. In fact it just does not give a lot of trouble because PHP codes are what I always say, just simple scripts. If they were complex applications indeed many classes would start exploding. In general its use is very contained and deep cloning is not usually necessary. Where they are needed are usually written by programmers who have more notion of how computing functions as a whole.

Clone! = Copy

Note that cloning, even shallow, is different from copying the object, the pure copy only copies the reference of the object, not the object.

Examples

In the example of the question $b will have an object equal to $a , but will be another object. They will be completely independent, each with its own life, changing one does not change the other. You cloned and not simply copied. Whether the cloning will be shallow or deep depends on the object type of the $a variable. As far as I know, ArrayObject does not clone deeply, at least there's nothing to say this in documentation / a>.

In class X you are doing this with your member, ensuring that a copy of the member is made and not just the reference.

$x = new X(new Y());
$y = clone $x;

In this case you already know that $x and $y will be independent. But most importantly, internally the y member, accessed by $this will also be independent in each of the objects, ie this member will be copied too. If I did not have this cloning. Both $x.y , and $y.y would point to the same object, and changing in one, would change in the other, they would not be independent. It probably was not what I wanted.

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}

class B {}

class C extends A {
    public function __clone() {
        $this->b = clone $this->b;
    }
}

$a = new A(new B);
$aa = clone $a;
$b = $a;
$c = new C(new B);
$cc = clone $c;
echo "CÓPIA\n";
var_dump($a === $b); //é igual, ambos apontam para o mesmo objeto
var_dump($a->b === $b->b); //continua igual, é o mesmo objeto, não pode ser diferente
echo "SEM __CLONE\n";
var_dump($a === $aa); //é diferente, copiou o objeto
var_dump($a->b === $aa->b); //é igual, o membro continua sendo o mesmo objeto apontado
echo "COM __CLONE\n";
var_dump($c === $cc); //é diferente, copiou o objeto
var_dump($c->b === $cc->b); //é diferente, copiou o objeto referenciado pelo membro

See working on ideone

The magic method __clone() is always called by the command clone of the language, when it is available for that object. If it is not available, cloning will be rare.

Documentation .

    
30.06.2016 / 16:11
7

Summary

Basically the magic method __clone() serves as a "callback" after a clone.

When an object is cloned the compiler searches for the magic method __clone() and, if it exists, it is invoked.

Independent instances and assignment by reference

A cloned object is independent of the original it was cloned from.

However, you may want to copy or make assignments by reference to the members of the original object. For this, there is the magic method __clone() where you can make these copies.

The reason that a deep and complete copy of an entire object does not occur is that you do not always need a deep copy of the entire object. At least not within the primary purpose of use we make of PHP.

In the PHP manual you have examples good enough to understand: link

On the two issues at the end

  

There is some case where it is really necessary to define a   behavior for cloning an object?

     

Why do some libraries use __clone to clone a property   corresponding to an instance of an object (as in example 2)?

It's complicated to try to answer because the reason / circumstance depends on a context.

Without understanding the context it becomes impracticable to respond.

    
30.06.2016 / 16:47
-4
  

After cloning completes, if a __clone () method is defined, the newly created object will have its __clone () method called, allowing any property to be changed.

     

link

Nothing more, nothing less.

// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
    
30.06.2016 / 15:59