What is the purpose of The Clone magic method?

In PHP we have the magic method __clone. It serves to define a behavior when you clone an object, through the Keyword clone.

So far so good. But I did not understand very well why have this magic method, since the word clone by itself 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 observations, 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)?

Author: Maniero, 2016-06-30

3 answers

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 that object. Eventually one may want until every object tree is cloned, if 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 it. This happens a lot when you hold external resources in the class, such as files and GUI objects, but it's not limited to these things. Anything that needs full data independence needs deep cloning.

Shallow clone

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

Then a lot of people start getting lost by creating classes. Most programmers do not understand all the implications of creating a class. It usually works because in general classes do not do anything sophisticated, or even they were not even needed in fact. Actually it just doesn't give a lot of trouble because PHP codes are what I always talk about, just simple scripts. If they were complex applications in fact too many classes would start exploding. In general its use is very restrained and deep cloning is usually not necessary. Where they are needed is often written by programmers who have more understanding of how computing works as a whole.

Clone != Copy

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

Examples

In the question example $b will have an object same as $a, but it 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 cloning will be shallow or deep depends on the object type of the variable $a. As far as I know ArrayObject doesn't clone deeply, at least it doesn't have anything that says this in the documentation.

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

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

In this case you already know that $x and $y will be independent. But the most important thing is that internally the member y, accessed by the $this will also be independent in each of the objects, that is, this member will be copied as well. If I didn't have that 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. Probably 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 running on ideone. E no repl.it. also I put on GitHub for future reference .

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

Documentation .

 17
Author: Maniero, 2020-11-03 18:28:09

Summary

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

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

Independent instances and assignment by reference

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

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

The reason for not having a deep and complete copy of an entire object is that you don't always need a deep copy of the entire object. At least not within the main purpose of use we make of PHP.

In the PHP manual you have examples good enough to understand: http://php.net/manual/pt_BR/language.oop5.cloning.php

On the two questions at the end

Is there any case where it is, actually, 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 is complicated to try to answer because the reason/reason / circumstance depends on a context.

Without understanding the context it becomes unfeasible to answer.

 7
Author: Daniel Omine, 2020-06-11 14:45:34

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

Http://php.net/manual/pt_BR/language.oop5.cloning.php

Nothing more, nothing less.

// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
 -5
Author: Ademílson F. Tonato, 2020-06-11 14:45:34