PHP Object Assignment vs Cloning

The difference between

 $assigned = $instance 

and

 $assigned = clone $instance

is that in first case you assign a reference of already existing object and in second one you create a new object and assign it to the variable.

Moreover, when you use clone keyword you can use magic method __clone() which gives you better control on object cloning. From php manual:

Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method will be called, to allow any necessary properties that need to be changed.

From manual:

A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.

Let me give you a live example

$dateA = new \Datetime('2017-04-04');

$dateB = $dateA; // $dateB references exactly the same object as $dateA

$dateB->modify('+1 day');

var_dump($dateA->format('Y-m-d')); //string(10) "2017-04-05" 
var_dump($dateB->format('Y-m-d')); //string(10) "2017-04-05"


// $dateA is still modified by the code above so it has 2017-04-05
$dateC = clone $dateA; // we clone $dateA so it's a new object

$dateC->modify('+1 day');

var_dump($dateA->format('Y-m-d')); // string(10) "2017-04-05" 
var_dump($dateC->format('Y-m-d')); // string(10) "2017-04-06"

// side note for datetime I recommend using DatetimeImmutable instead of Datetime

EDIT: internal types

// create 2 integer variables $a and $b

$a = 1;
$b = 1;

// create a new $c variable and assign the *value* of $a to that variable
$c = $a;

// create a new $d variable and assign a reference to $b variable
$d = &$b;

// increment $b, $c and $d variables
$b++; 
$c++;
$d++;

echo $a; // 1
echo $b; // 3
echo $c; // 2
echo $d; // 3

because $d references $b when we increment its value it'll also change the value of $b.

The difference between internal objects like strings, int, float etc. is that they are passed by value and objects are passed by default via reference

Notice: You cannot use clone with internal objects.


well, basically those variables are nothing but pointers to the memory space, where object resides. If you store pointer value in another pointer, and then reset original pointer, nothing will happen to the memory area they both once pointed to.


Objects are abstract data in memory. A variable always holds a reference to this data in memory. Imagine that $foo = new Bar creates an object instance of Bar somewhere in memory, assigns it some id #42, and $foo now holds this #42 as reference to this object. Assigning this reference to other variables by reference or normally works the same as with any other values. Many variables can hold a copy of this reference, but all point to the same object.

clone explicitly creates a copy of the object itself, not just of the reference that points to the object.

$foo = new Bar;   // $foo holds a reference to an instance of Bar
$bar = $foo;      // $bar holds a copy of the reference to the instance of Bar
$baz =& $foo;     // $baz references the same reference to the instance of Bar as $foo

Just don't confuse "reference" as in =& with "reference" as in object identifier.

$blarg = clone $foo;  // the instance of Bar that $foo referenced was copied
                      // into a new instance of Bar and $blarg now holds a reference
                      // to that new instance

PHP doesn't manage the objects the same way it manages the other data types. A string (or an integer, boolean, float or array) is directly stored in the variable. When the value of the variable is assigned to another variable, the value is copied1 into the new variable.

For example:

$x = array('a');
$y = $x;
// $x and $y are different and unrelated variables; they do not share anything
$y[] = 'b';
print_r($y);
// Array
// (
//     [0] => a
//     [1] => b
// )
print_r($x);
// Array
// (
//     [0] => a
// )

How PHP handles the objects assignment?

The objects, on the other hand, are handled by PHP using unique identifiers. When an object is assigned to a variable, the identifier is stored in the variable and not the actual object.

When the value of the variable is assigned to another variable, the identifier is copied and not the object itself. This makes the two variables point to the same object.

Using your example, the values of variables $instance and $assigned are equal, they both contain the identifier of the same object. $reference, on the other side, is a reference, i.e. an alias (a different name) of variable $assigned. This is why the statement $instance = null; clears the content of variables $reference and $assigned but it doesn't affect the variable $instance and also the object whose identifier is stored in it.

Before setting $reference to null, one can use any of $instance, $assigned or reference to access the SimpleClass object created on the first line of your example. F.e:

$instance = new SimpleClass();
$assigned = $instance;

$instance->var = '$assigned will have this value';
echo($instance->var);
// It prints: 
// $assigned will have this value

// $assigned is also modified because it is the same object
echo($assigned->var);
// It prints: 
// $assigned will have this value

Read more about PHP objects and references in the documentation.

What happens when $assigned = clone $instance?

The clone operator creates a duplicate of its operand. It creates a new object and it initializes all its properties by assigning the values of the properties of the original object to them. This means that if the clone object contains objects as properties, these properties are duplicated by simple assignment, not by cloning.2

$assigned = clone $instance;

After this statement, $assigned contains a different value than $instance because they store now the IDs of different objects. Being different objects, the changes of $instance do not affect $assigned any more.

$instance = new SimpleClass();
$instance->var = '$instance has this value';

$assigned = clone $instance;
echo($assigned->var);
// It prints: 
// $instance has this value

$assigned->var = '$assigned has a different value';
echo($assigned->var);
// It prints:
// $assigned has a different value

// $instance is not modified
echo($instance->var);
// It prints: 
// $instance has this value

1 This is not entirely true. For optimization purposes, the arrays are not copied until they are modified (copy-on-write). However, this is an implementation detail and for the purpose of this discussion it is fine to consider all values except the objects are copied when they are assigned to a new variable.

2 This is also called "shallow" cloning. In order to get a "deep" clone (a real duplicate that doesn't share anything with the original), the class of a cloned object that has objects as properties must implement the __clone() magic method and clone the properties of the original object. Also, the classes of these properties must implement __clone() and so on.

Tags:

Php

Object