A variable holds an object handle
As you already know, $b = $a copies a whole array: a write to one array is not seen in the other. Let's check what the same assignment does with an object:
<?php
class Point
{
public int $x = 1;
}
$a = new Point();
$b = $a;
$b->x = 5;
echo $a->x, "\n"; // 5The write went through $b, and $a changed too. The object was not copied: both variables lead to one and the same object.
Let's go back to the houses: a variable holds not the house itself but its address. $b = $a copies the address onto a second slip of paper, and now you have two slips that lead to one house. Whatever you change in the house after coming by one slip, anyone who comes by the other one will see.
The object handle
An object handle is the number PHP finds an object by. That is what a variable holds. new builds an object and hands out its handle, while $b = $a copies the handle, and there is still one object.
A separate copy of an object is made by clone: it builds a second house modeled on the first. Press "Next step" and watch the variables in the {main} frame. Point#1 and Point#2 are two different objects, and the same number in two variables means there is one object.
1<?php2 3class Point4{5 public int $x = 1;6}7 8$a = new Point();9$b = $a;10$b->x = 5;11$c = clone $a;12$c->x = 9;13echo $a->x, ' ', $b->x, "\n";14echo $c->x, "\n";
new builds the object Point#1, and $a gets its handle.
The program printed 5 5 and 9. $a and $b are two names of the object Point#1, so the write through $b is seen through $a. clone $a built the object Point#2 with its own copy of the property x, and from then on the copy lives apart.
Interviewers like to ask this: "Are objects passed by reference in PHP?" The answer is no:
- a variable holds an object handle, and assigning it or passing it to a function copies that handle;
- so a function that receives an object changes the same object the calling code holds;
- the reference
&is something else: it makes the parameter a second name of the variable itself.
=== and == for objects
For objects, === looks at the handle, while == compares the class and the properties:
$c = clone $a;
var_dump($a === $b); // bool(true)
var_dump($a === $c); // bool(false)
var_dump($a == $c); // bool(true)What happens here:
$a === $bgivestrue: it is one and the same object.$a === $cgivesfalse: the copy is another object, even though its values are the same.$a == $cgivestrue: loose comparison checks that the objects are of one class and their properties are equal.
clone copies shallowly
Here the house picture needs a caveat: clone builds a second house, but the two houses share the furniture. If a property holds another object, clone copies that object's handle, not the object itself:
class Sofa
{
public string $color = 'grey';
}
class House
{
public function __construct(public Sofa $sofa) {}
}
$first = new House(new Sofa());
$second = clone $first;
$second->sofa->color = 'red';
echo $first->sofa->color, "\n"; // redA shallow copy is a copy in which the properties are copied and the nested objects stay shared. The sofa was repainted through $second, and in $first it is red too. If a nested object must be copied along with the outer one, the class declares the method __clone: PHP calls it on the new copy right after clone.
Common mistake
Before changing an object, people "save a copy" with a plain assignment:
$backup = $a; // ✗
$a->x = 0;
echo $backup->x, "\n"; // 0There is no message, but the "copy" changed together with the original: $backup is a second name of the same object. A copy is made with clone: after $backup = clone $a; the value of $backup->x stays 5.
Key points
- A variable with an object holds an object handle, not the object itself.
$b = $acopies the handle: two names lead to one object, and a write through one is seen through the other.clonebuilds a second object, but the nested objects in it stay shared.===givestrueonly for one and the same object, while==compares the class and the properties.