TL;DR >
echo (new ClassName())->propertyName;
Will call the __destruct(), run its code and AFTER THAT successfully retrieve its "propertyName" property (which will be echoed normally). How can it be retrieving a property from a "destroyed" (or unset) object?
< TL;DR
I have tried to find this online (sorry if it's been already answered). The explanation on https://www.php.net/manual/en/language.oop5.decon.php didn't quite explain this to me.
I'm using https://onlinephp.io/ to test this. Just for the sake of it, I'm using both PHP versions 7.0.33 and 8.1.8 (but they seem to behave the same way about it).
The code is as follows:
class Fruit {
public $name;
public function __construct($n = "Fruit") {
$this->name = $n;
}
public function __destruct() {
echo "\nBye bye fruit\n";
}
}
Given this Fruit class, I will create 2 instances of it - Apple and Banana - and retrieve and echo its "name" property.
Apple will be assigned to a variable.
$apple = new Fruit("Apple");
unset($apple);
echo $apple->name . "\n";
This will echo the "Bye bye fruit" as expected (from the __destruct()), and then give me a warning, since I am trying to access its property after the object has been unset.
Bye bye fruit
Warning: Undefined variable $apple in /home/user/scripts/code.php on line X
Warning: Attempt to read property "name" on null in /home/user/scripts/code.php on line X
That's expected, since there's no $apple anymore.
HOWEVER
echo (new Fruit("Banana"))->name;
Will output:
Bye bye fruit
Banana
I'm not assigning it to any variable, so I'm assuming it's being destroyed (unset?) by the garbage collector.
Nonetheless, the __destruct() is being called, as we can see the output "Bye bye fruit". If it had output (echoed) the property ("Banana") first, I'd understand, but this seems counterintuitive to me.
How is it possible that the property is being accessed? Where does it "live"?
P.S.
I did find this 14 years old comment on the php.net reference page (link above)
[...]
public static function destroyAfter(&$obj)
{
self::getInstance()->objs[] =& $obj;
/*
Hopefully by forcing a reference to another object to exist
inside this class, the referenced object will need to be destroyed
before garbage collection can occur on this object. This will force
this object's destruct method to be fired AFTER the destructors of
all the objects referenced here.
*/
}
[...]
But, even if it give any clue, it doesn't seem to be THE explanation, because:
He is creating a specific method (unless adding a reference anywhere in the class will force the entire class to change its behaviour)
Even if properties inside a class have their own "reference", how can the application still find it "through the object", given that it was already destroyed?