If I deep clone an object that contains another object while I have a reference in a variable of the inner property, the clone will not be deep.
<?php
class Person
{
public function __construct(
public string $name,
) {
}
}
class Country
{
public function __construct(
public Person $person,
) {
}
public function __clone()
{
$this->person = clone $this->person;
}
}
$usa = new Country(new Person('Arthur'));
$blah = &$usa->person;
$italy = clone $usa;
$italy->person->name = 'jack';
var_dump($usa);
var_dump($italy);
This line cause $usa
and $italy
to contain the same Person
property. Doing unset $blah
before cloning $usa works, but why ?
$blah = &$usa->person;
Outputs :
object(Country)#1 (1) {
["person"]=>
&object(Person)#4 (1) {
["name"]=>
string(4) "jack"
}
}
object(Country)#3 (1) {
["person"]=>
&object(Person)#4 (1) {
["name"]=>
string(4) "jack"
}
}
It is related on how php treats "Reference"
you can find more info here:
https://www.php.net/manual/en/language.references.whatare.php
and here:
https://www.php.net/manual/en/language.references.arent.php
but to make things short when you create a reference php convert both variables (object or whatever) from 'value types' to 'reference type'
so if you create a reference and after make a clone since both $bla e $usa->person are 'reference type' you will end up cloning the reference
if you delete all references except one php will convert the last object/variable into a 'value type' again.
one last thing ... i dont know any way to identify 'reference types' at runtime by code and i dont think there's one. but if you var_dump your variables you'll identify them easily:
object(Country)#1 (1) {
["person"]=>
//'value type' since no & prefix
//==============================
object(Person)#2 (1) {
["name"]=>
string(6) "Arthur"
}
}
object(Country)#3 (1) {
["person"]=>
//'value type' since no & prefix
//==============================
object(Person)#4 (1) {
["name"]=>
string(4) "jack"
}
}
object(Country)#1 (1) {
["person"]=>
//'reference type' ===> & prefix
//==============================
&object(Person)#4 (1) {
["name"]=>
string(4) "jack"
}
}
object(Country)#3 (1) {
["person"]=>
//'reference type' ===> & prefix
//==============================
&object(Person)#4 (1) {
["name"]=>
string(4) "jack"
}
}