PHP since version 5 implements magic methods you can implement in your classes, which will be automatically called by your script. Magic method __clone () is one of these methods. The following tutorial introduces the operation of the magic method __clone () based on simple and concrete examples.
We’ll be covering the following topics in this tutorial:
Reminders on object-oriented programming
To understand the principle of this method and its scope, this is how running (in outline), object-oriented programming.
Take an object “Point” that has protected properties “_x” and “_y”, which here correspond to the coordinates of the point. It adds a setcoords () method that lets you specify these values:
<? php
class Point
{
/ **
* X-coordinate of the point
*
*var Integer
* /
protected $ _x = 0;
/ **
* Intercept point
*
*var Integer
* /
protected $ _y = 0;
/ **
* Fixed the point coordinates
*
*param Integer $ x coordinate of the point
*param Integer $ y coordinate of the point
*return Void
* /
public function setcoords ($ x, $ y) {
$ this -> _ x = (int) $ x;
$ this -> _ y = (int) $ y;
}
}
When you want to use this object, we call its constructor (here it is declared implicitly) and we store it in a variable the result of this appeal. This is what we call the class instantiation phase. The variable is an instance of this object.
<? php
// Instantiating the object Point
// And storage in the instance variable "ODOT"
$ ODOT = new Point ();
?>
PHP 5 and the passing objects by reference
That’s the outline. Now speak (sorry) PHP 4. With version 4 of PHP, objects were passed by “value” (or by “copy”), which means that when variable “$ a” containing the instance of a Point object was copied into a variable $ b, then the two variables $ a and $ b each contained an instance (object) unique. PHP 5, objects are moved and copied by reference. This implies that if a copy of a $ a to $ b, it will contain the reference refers to the object in $ a. Let’s take a simple example to illustrate the theory in practice.
<? php
$ ODOT = new Point;
$ oDot-> setcoords (10,10);
var_dump ($ ODOT);
$oNewDot = $ ODOT;
$ oNewDot-> setcoords (20,20);
var_dump ($ ODOT);
?>
Here we instantiate our Point object and we store the instance in $ ODOT. Then we copy the contents of the variable $ ODOT in $ oNewDot. We modify the instance properties of the object stored in $ oNewDot Point and finally we display the information of the instance of our object.
The first var_dump () appears:
object (Point) # 1 (2) {["_x: protected"] => int (10) ["_y: protected"] => int (10)}
The second returns:
object (Point) # 1 (2) {["_x: protected"] => int (20) ["_y: protected"] => int (20)}
We note here that we are dealing with a single instance although we have tried to copy the variable.
But then, in some cases it may be useful to duplicate a class instance, which is not possible through the ‘PHP 4 “method, as we have just seen. The solution is to use the keyword “clone”.
The clone keyword
Operation is simple, instead of:
<? php
$ oNewDot = $ ODOT;
?>
We will do :
<? php
$ oNewDot = clone $ ODOT;
?>
By modifying and running the previous code:
<? php
$ ODOT = new Point;
$ oDot-> setcoords (10,10);
var_dump ($ ODOT);
$ oNewDot = clone $ ODOT;
$ oNewDot-> setcoords (20,20);
var_dump ($ oNewDot);
var_dump ($ ODOT);
?>
Here the display of the first var_dump ():
object (Point) # 1 (2) {["_x: protected"] => int (10) ["_y: protected"] => int (10)}
And here is the display of the second, we can see that the _x and _y properties are modified:
object (Point) # 1 (2) {["_x: protected"] => int (20) ["_y: protected"] => int (20)}
Here the display of the third and final var_dump:
object (Point) # 1 (2) {["_x: protected"] => int (10) ["_y: protected"] => int (10)}
You can see for yourself, we have indeed two separate instances of the Point class.
Implementation of the magic method __clone
Now that we have understood the concept of cloning objects in PHP, we will finally be able to talk about the magic method “__clone ()”. The principle of this method is simple, it will be automatically called when the clone keyword will be used on an object. Let’s take an example.
Here is a class Sheep (sheep, for anglophobes):
<? php
class Sheep
{
/ **
* Sheep Name
*
*var String
* /
protected $ _name;
/ **
* Building & the Sheep class
*
*param String $ name name of sheep
* /
public function __construct ($ name) {
$ this -> _ name = (string) $ name;
}
/ **
* Magic clone Method
*
*return Void
* /
public function __clone() {
$ this -> _ name = 'Copy' $ this -> _ name.
}
}
Instantiate our class, and our duplicat object:
<? php
$ oSheep = new Sheep (Dolly);
$ oNewSheep = clone $ oSheep;
var_dump ($ oSheep);
var_dump ($ oNewSheep);
?>
Here is the first displayed by var_dump ():
object (Sheep) # 3 (1) {["_name: protected"] => string (5) "Dolly"}
And here is the second displayed by var_dump ():
object (Sheep) # 4 (1) {["_name: protected"] => string (14) "Copy Dolly"}
By duplicating the instance ‘$ oSheep, my method was __clone automatically called and changed the name by adding “Copy of” prefix in the name of the sheep.
You will notice that we have used the reference to the object itself $ this to change the information of the new instance.
Implementation in the case of a Singleton
The Singleton design pattern is to ensure that only have one instance of an object in a script. For more information, please visit this post.
But even if your constructor is declared protected or private access, it will still be possible to clone your object. You will lose the principle of singleton. To overcome this problem, just throw an exception when the developer using the Singleton decides to clone:
<? php
class Singleton {
/ **
* Instance of the Singleton class
*
*var Singleton
* /
protected static $ _instance = null;
/ **
* Class Constructor
*
* @ Access protected
* /
protected function __construct () {}
/ **
* GetInstance (): recovery of the class instance
*
*return Singleton
* /
public static function getInstance () {
if (null === self :: $ _ instance) self :: $ _ instance = new Singleton ();
return self :: $ _ instance;
}
/ **
* Magic clone Method
*
*return Void
* /
public function __clone () {
throw new Exception ('Are you Trying to clone me I \?' Singleton my dude! ');
}
}
try {
$ oSingleton = Singleton :: getInstance ();
clone $ oSingleton ;
} catch (Exception $ e) {
echo 'Oops, except:', $ e-> getMessage ();
}
?>
And this is what will appear in the execution of the code:
Oops, exception: Are you Trying to clone me? I’m a Singleton dude!
The exception has been raised and has prevented the attempted cloning of the object. The singleton and retain its uniqueness in the developed application.