I was trying to make a "pool" like structure in a CLI program, which includes lots of "borrowing" and "recycling". And while testing things, I encountered something quite unexpected:
<?php
class FOO
{
public static $pool=[];
public static function get()
{
if(empty(self::$pool))
{
self::$pool[]=new self(mt_rand(1000,9999));
}
return array_shift(self::$pool);
}
protected static function recycle(FOO $foo)
{
echo "Recycling {$foo->num}\n";
self::$pool[]=$foo;
}
public $num;
protected function __construct(int $num)
{
$this->num=$num;
}
public function __destruct()
{
static::recycle($this);
}
}
function Bar()
{
$foo=FOO::get();
echo "Got {$foo->num}\n";
}
echo "Bar\n";
Bar();
echo "Bar\n";
Bar();
echo "Bar\n";
Bar();
echo "Bar\n";
Bar();
print_r(FOO::$pool);
echo "End.\n";
The output is:
Bar
Got 2911
Recycling 2911
Bar
Got 2911
Bar
Got 1038
Recycling 1038
Bar
Got 1038
Array
(
)
End.
If I limit the Bar() call to 3 times instead of 4, I got the following output:
Bar
Got 7278
Recycling 7278
Bar
Got 7278
Bar
Got 6703
Recycling 6703
Array
(
[0] => FOO Object
(
[num] => 6703
)
)
End.
Here you can see, when an object is "reused" (see 2911 and 1038 in example 1, 7278 in example 2), its __destruct() will not be called; instead, it will simply disappear.
I'm confused. Why would this happen? Why wouldn't FOO->__destruct got called every time? And is it possible to make instances of FOO auto recycle itself?
I'm testing this in PHP-7.2, behavior observed both in Windows and WSL-Ubuntu.
As far as I know destructors will not be called twice for the same object. It is generally a bad practice to reuse objects from the destructor. An object whose destructor was called should be destroyed, not reused. In your test script it doesn't cause any serious issues, but in real life, such usage could lead to unexpected behaviours if the developer is not careful.
At first when I read your question, I was worried that you have created a memory leak, but that is not the case. The destructor is called as soon as you leave the scope of Bar() only if it hasn't been called yet on this object. However, because you save the reference and increase the ref_count of this object inside of the __destruct() the garbage collector cannot collect the object yet. It must wait until the next time when you decrease the ref_count of this object.
The process can be explained as follows (simplified):
You call Bar() function which creates an instance of FOO. An object is created and assigned to $foo. (ref_count = 1)
Upon the end of Bar() the only reference to this object is lost (ref_count = 0), which means PHP starts the process of destroying the object.
2.1. The destructor is called. Inside of the destructor you increase the ref_count to 1.
2.2. The next step would be for the GC to collect the object, but the ref_count is not zero. This could mean a cycle or like in your example, a new reference has been created in the destructor. GC must wait until there are no uncyclical references to collect the object.
You call Bar() again. You shift the same object from static array, which means that the reference inside of FOO::$pool is gone, but you immediately assign it to $foo. (ref_count = 1)
Upon the end of Bar() the only reference to this object is lost (ref_count = 0), which means GC can finally collect this object. The destructor has already been called, so there is no other action to be taken here.
You didn't recycle the object, you merely prolonged its existance in the memory. You could access it for a little longer, but for PHP that object was already in the process of being destroyed.
Related
I have a simple class and assign its instance to its static variable. When I unset the instance, not sure why it doesn't actual release the memory.
class Foo {
public static $app;
public function __construct() {
self::$app = $this;
}
}
$foo = new Foo;
unset($foo);
var_dump($foo);
var_dump(Foo::$app);
Result:
Warning: Undefined variable $foo in /var/www/index.php on line 16
NULL object(Foo)#1 (0) { }
Obviously, the static $app point to the itself instance, we have unset the instance but doesn't really clean the memory for this variable. Why did it happen? Are the instance of the $app and the $foo different ones?
$foo and Foo::$app point to the same object as you desire. Any changes in terms of object properties will reflect either way if you do it.
This is because they point to the same copy in the memory as expected. However, PHP maintains a refcount indicating how many references to that memory location is present.
So, when you use unset($foo);, it doesn't garbage collect that memory location(and the value inside it), but rather just decreases the refcount of it. This can be proved using debug_zval_dump and PHP achieves the refcount maintenance using copy on write mechanism as mentioned in the linked doc.
Hence, unsetting $foo doesn't destroy the object itself but rather just the refcount and removes the variable from memory since the object still holds other references.
Snippet:
<?php
class Foo {
public static $app;
public function __construct() {
self::$app = $this;
}
}
$foo = new Foo;
var_dump($foo === Foo::$app);
debug_zval_dump(Foo::$app);
$foo = 90;// variable modified, copy on write mechanism done internally for Foo::$app, refcount modified
debug_zval_dump(Foo::$app);
Online Demo
You've only unset a reference to an object of class Foo, and that object will get garbage-collected next time garbage collector runs.
Classes, once loaded, live in the memory until request is terminated, and so will their static properties. If they hold a pointer to an object, that object won't be garbage-collected. If you want to garbage-collect Foo::$app, you'll need to unset it too.
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?
The destructor is actually being called after retrieving the property, but before echoing it.
If we get a representation of how the code is compiled, we can see that this line:
echo (new Fruit("Banana"))->name;
Compiles to this:
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
15 0 E > NEW $0 'Fruit'
1 SEND_VAL_EX 'Banana'
2 DO_FCALL 0
3 FETCH_OBJ_R ~2 $0, 'name'
4 ECHO ~2
16 5 > RETURN 1
Without going into too much detail:
$0 is the internal variable holding the object: it's created by the NEW opcode, and then used by the FETCH_OBJ_R opcode to look up the 'name' property
~2 is the internal variable holding the result of the FETCH_OBJ_R opcode
the ECHO opcode only needs ~2, not $0, so between these two operations, $0 will be discarded, and the destructor triggered
In other words, it's roughly equivalent to this code:
$_0 = (new Fruit("Banana"));
$_2 = $_0->name;
unset($_0);
echo $_2;
Another way to see this is by replacing the property access with a method call:
class Fruit {
private $name;
public function __construct($n = "Fruit") {
$this->name = $n;
}
public function getName() {
echo "\nGetting name...\n";
return $this->name;
}
public function __destruct() {
echo "\nBye bye fruit\n";
}
}
echo (new Fruit("Banana"))->getName();
From the output, it's clear that although we don't see the name until after the object is destroyed, it was retrieved first:
Getting name...
Bye bye fruit
Banana
It looks backwards, but it does make sense. What's happening is
PHP passes the value to echo, so that "method" (it's really a language construct) has been passed a copy of the value to output. Remember, PHP passes by value
Upon doing so, there are no remaining references to the class, so garbage collection kicks in and destroys the class, dutifully calling the destructor
echo executes with the value it's already been given
If you assign your class to a variable, it works the other way around
$fruit = new Fruit("Banana");
echo $fruit->name;
produces this result
Banana
Bye bye fruit
The difference is that the script has to end first before the object is destroyed. It also happens this way if you use the class in a function (unlike variables, classes are always passed by reference). This does the same thing because the function is getting the created instance of the class. Thus the function has to end before the destructor can be called.
function showFruit(Fruit $fruit) {
echo $fruit->name;
}
showFruit(new Fruit("Banana"));
What is the exact order of object deconstruction?
From testing, I have an idea: FIFO for the current scope.
class test1
{
public function __destruct()
{
echo "test1\n";
}
}
class test2
{
public function __destruct()
{
echo "test2\n";
}
}
$a = new test1();
$b = new test2();
Which produces the same results time and time again:
test1
test2
The PHP manual is vague (emphasis mine to highlight uncertainty): "The destructor method will be called as soon as there are no other references to a particular object or in any order during the shutdown sequence."
What is the exact order of deconstruction? Can anyone describe in details the implementation of destruction order that PHP uses? And, if this order is not consistent between all the PHP versions, can anyone pinpoint which PHP versions change in this order?
First of all, a bit on general object destruction order is covered here: https://stackoverflow.com/a/8565887/385378
In this answer I will only concern myself with what happens when objects are still alive during the request shutdown, i.e. if they were not previously destroyed through the refcounting mechanism or the circular garbage collector.
The PHP request shutdown is handled in the php_request_shutdown function. The first step during the shutdown is calling the registered shutdown functions and subsequently freeing them. This can obviously also result in objects being destructed if one of the shutdown functions was holding the last reference to some object (or if the shutdown function itself was an object, e.g. a closure).
After the shutdown functions have run the next step is the one interesting to you: PHP will run zend_call_destructors, which then invokes shutdown_destructors. This function will (try to) call all destructors in three steps:
First PHP will try to destroy the objects in the global symbol table. The way in which this happens is rather interesting, so I reproduced the code below:
int symbols;
do {
symbols = zend_hash_num_elements(&EG(symbol_table));
zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC);
} while (symbols != zend_hash_num_elements(&EG(symbol_table)));
The zend_hash_reverse_apply function will walk the symbol table backwards, i.e. start with the variable that was created last and going towards the variable that was created first. While walking it will destroy all objects with refcount 1. This iteration is performed until no further objects are destroyed with it.
So what this basically does is a) remove all unused objects in the global symbol table b) if there are new unused objects, remove them too c) and so on. This way of destruction is used so objects can depend on other objects in the destructor. This usually works fine, unless the objects in the global scope have complicated (e.g. circular) interrelations.
The destruction of the global symbol table differs significantly from the destruction of all other symbol tables. Normally symbol tables are destructed by walking them forward and just dropping the refcount on all objects. For the global symbol table on the other hand PHP uses a smarter algorithm that tries to respect object dependencies.
The second step is calling all remaining destructors:
zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC);
This will walk all objects (in order of creation) and call their destructor. Note that this only calls the "dtor" handler, but not the "free" handler. This distinction is internally important and basically means that PHP will only call __destruct, but will not actually destroy the object (or even change its refcount). So if other objects reference the dtored object, it will still be available (even though the destructor was already called). They will be using some kind of "half-destroyed" object, in a sense (see example below).
In case the execution is stopped while calling the destructors (e.g. due to a die) the remaining destructors are not called. Instead PHP will mark the objects are already destructed:
zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC);
The important lesson here is that in PHP a destructor is not necessarily called. The cases when this happens are rather rare, but it can happen. Furthermore this means that after this point no more destructors will be called, so the remainder of the (rather complicated) shutdown procedure does not matter anymore. At some point during the shutdown all the objects will be freed, but as the destructors have already been called this is not noticeable for userland.
I should point out that this is the shutdown order as it currently is. This has changed in the past and may change in the future. It's not something you should rely on.
Example for using an already destructed object
Here is an example showing that it is sometimes possible to use an object that already had its destructor called:
<?php
class A {
public $state = 'not destructed';
public function __destruct() { $this->state = 'destructed'; }
}
class B {
protected $a;
public function __construct(A $a) { $this->a = $a; }
public function __destruct() { var_dump($this->a->state); }
}
$a = new A;
$b = new B($a);
// prevent early destruction by binding to an error handler (one of the last things that is freed)
set_error_handler(function() use($b) {});
The above script will output destructed.
What is the exact order of deconstruction? Can anyone describe in detail the implementation of destruction order that PHP uses? And, if this order is not consistent between any and all PHP versions, can anyone pinpoint which PHP versions this order changes in?
I can answer three of these for you, in a somewhat roundabout way.
The exact order of destruction is not always clear, but is always consistent given a single script and PHP version. That is, the same script running with the same parameters that creates objects in the same order will basically always get the same destruction order as long as it runs on the same PHP version.
The shutdown process -- the thing that triggers object destruction when script execution has stopped -- has changed in the recent past, at least twice in a way that impacted the destruction order indirectly. One of these two introduced bugs in some old code I had to maintain.
The big one was back in 5.1. Prior to 5.1, the user's session was written to disk at the very start of the shutdown sequence, before object destruction. This meant that session handlers could access anything that was left over object-wise, like, say, custom database access objects. In 5.1, sessions were written after one sweep of object destruction. In order to retain the previous behavior, you had to manually register a shutdown function (which are run in order of definition at the start of shutdown before destruction) in order to successfully write session data if the write routines needed a (global) object.
It is not clear if the 5.1 change was intended or was a bug. I've seen both claimed.
The next change was in 5.3, with the introduction of the new garbage collection system. While the order of operations at shutdown remained the same, the precise order of destruction could now change based on ref counting and other delightful horrors.
NikiC's answer has details on the current (at time of writing) internal implementation of the shutdown process.
Once again, this is not guaranteed anywhere, and the documentation very expressly tells you to never assume a destruction order.
For anyone interested - as at PHP 8.0:
class A {
function __destruct() {
print get_class();
}
}
class B {
private $child;
function __construct() {
$this->child = new A();
}
function __destruct() {
print get_class();
}
}
class C {
private $child;
function __construct() {
$this->child = new B();
}
function __destruct() {
print get_class();
}
}
new C;
results in output of
CBA
ie. the containing object destructor fires before the contained object destructor.
To reverse the order if desired ie. to ABC, change the destructor in all but A (innermost class) to be:
function __destruct() {
unset($this->child);
print get_class();
}
I am working with classes and object class structure, but not at a complex level – just classes and functions, then, in one place, instantiation.
As to __construct and __destruct, please tell me very simply: what is the purpose of constructors and destructors?
I know the school level theoretical explanation, but i am expecting something like in real world, as in which situations we have to use them.
Provide also an example, please.
Regards
A constructor is a function that is executed after the object has been initialized (its memory allocated, instance properties copied etc.). Its purpose is to put the object in a valid state.
Frequently, an object, to be in an usable state, requires some data. The purpose of the constructor is to force this data to be given to the object at instantiation time and disallow any instances without such data.
Consider a simple class that encapsulates a string and has a method that returns the length of this string. One possible implementation would be:
class StringWrapper {
private $str;
public function setInnerString($str) {
$this->str = (string) $str;
}
public function getLength() {
if ($this->str === null)
throw new RuntimeException("Invalid state.");
return strlen($this->str);
}
}
In order to be in a valid state, this function requires setInnerString to be called before getLength. By using a constructor, you can force all the instances to be in a good state when getLength is called:
class StringWrapper {
private $str;
public function __construct($str) {
$this->str = (string) $str;
}
public function getLength() {
return strlen($this->str);
}
}
You could also keep the setInnerString to allow the string to be changed after instantiation.
A destructor is called when an object is about to be freed from memory. Typically, it contains cleanup code (e.g. closing of file descriptors the object is holding). They are rare in PHP because PHP cleans all the resources held by the script when the script execution ends.
Learn by example:
class Person {
public $name;
public $surname;
public function __construct($name,$surname){
$this->name=$name;
$this->surname=$surname;
}
}
Why is this helpful? Because instead of:
$person = new Person();
$person->name='Christian';
$person->surname='Sciberras';
you can use:
$person = new Person('Christian','Sciberras');
Which is less code and looks cleaner!
Note: As the replies below correctly state, constructors/destructors are used for a wide variety of things, including: de/initialization of variables (especially when the the value is variable), memory de/allocation, invariants (could be surpassed) and cleaner code.
I'd also like to note that "cleaner code" is not just "sugar" but enhances readability, maintainability etc.
The constructor is run at the time you instantiate an instance of your class. So if you have a class Person:
class Person {
public $name = 'Bob'; // this is initialization
public $age;
public function __construct($name = '') {
if (!empty($name)) {
$this->name = $name;
}
}
public function introduce() {
echo "I'm {$this->name} and I'm {$this->age} years old\n";
}
public function __destruct() {
echo "Bye for now\n";
}
}
To demonstrate:
$person = new Person;
$person->age = 20;
$person->introduce();
// I'm Bob and I'm 20 years old
// Bye for now
We can override the default value set with initialization via the constructor argument:
$person = new Person('Fred');
$person->age = 20;
$person->introduce();
// if there are no other references to $person and
// unset($person) is called, the script ends
// or exit() is called __destruct() runs
unset($person);
// I'm Fred and I'm 20 years old
// Bye for now
Hopefully that helps demonstrate where the constructor and destructor are called, what are they useful for?
__construct() can default class members with resources or more complex data structures.
__destruct() can free resources like file and database handles.
The constructor is often used for class composition or constructor injection of required dependencies.
The constructor of a class defines what happens when you instantiate an object from this class. The destructor of a class defines what happens when you destroy the object instance.
See the PHP Manual on Constructors and Destructors:
PHP 5 allows developers to declare constructor methods for classes. Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
and
PHP 5 introduces a destructor concept similar to that of other object-oriented languages, such as C++. The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence.
In practise, you use the Constructor to put the object into a minimum valid state. That means you assign arguments passed to the constructor to the object properties. If your object uses some sort of data types that cannot be assigned directly as property, you create them here, e.g.
class Example
{
private $database;
private $storage;
public function __construct($database)
{
$this->database = $database;
$this->storage = new SplObjectStorage;
}
}
Note that in order to keep your objects testable, a constructor should not do any real work:
Work in the constructor such as: creating/initializing collaborators, communicating with other services, and logic to set up its own state removes seams needed for testing, forcing subclasses/mocks to inherit unwanted behavior. Too much work in the constructor prevents instantiation or altering collaborators in the test.
In the above Example, the $database is a collaborator. It has a lifecycle and purpose of it's own and may be a shared instance. You would not create this inside the constructor. On the other hand, the SplObjectStorage is an integral part of Example. It has the very same lifecycle and is not shared with other objects. Thus, it is okay to new it in the ctor.
Likewise, you use the destructor to clean up after your object. In most cases, this is unneeded because it is handled automatically by PHP. This is why you will see much more ctors than dtors in the wild.
I've found it was easiest to grasp when I thought about the new keyword before the constructor: it simply tells my variable a new object of its data type would be give to him, based on which constructor I call and what I pass into it, I can define to state of the object on arrival.
Without the new object, we would be living in the land of null, and crashes!
The Destructor is most obvious from a C++ stand point, where if you dont have a destructor method delete all the memory pointed to, it will stay used after the program exits causing leaks and lag on the clients OS untill next reboot.
I'm sure there's more than enough good information here, but another angle is always helpful from what I've noticed!
constructor is function of class which is executed automatically when object of class is created we need not to call that constructor separately we can say constructor as magic method because in php magic method begin with double underscore characters
im trying to understand php constructor and destructor behaviour. Everything goes as expected with the constructor but i am having trouble getting the destructor to fire implicitly. Ive done all the reading on php.net and related sites, but i cant find an answer to this question.
If i have a simple class, something like:
class test{
public function __construct(){
print "contructing<br>";
}
public function __destruct(){
print "destroying<br>";
}
}
and i call it with something like:
$t = new test;
it prints the constructor message. However, i'd expect that when the scripts ends and the page is rendered that the destructor should fire. Of course it doesnt.
If i call unset($t); when the scripts ends, of course the destructor fires, but is there a way to get it to fire implicitly?
This is pretty easy to test.
<?php
class DestructTestDummy {
protected $name;
function __construct($name) {
echo "Constructing $name\n";
$this->name = $name;
}
function __destruct() {
echo "Destructing $this->name\n";
//exit;
}
}
echo "Start script\n";
register_shutdown_function(function() {
echo "Shutdown function\n";
//exit
});
$a = new DestructTestDummy("Mr. Unset");
$b = new DestructTestDummy("Terminator 1");
$c = new DestructTestDummy("Terminator 2");
echo "Before unset\n";
unset($a);
echo "After unset\n";
echo "Before func\n";
call_user_func(function() {
$c = new DestructTestDummy("Mrs. Scopee");
});
echo "After func\n";
$b->__destruct();
exit("Exiting\n");
In PHP 5.5.12 this prints:
Start script
Constructing Mr. Unset
Constructing Terminator 1
Constructing Terminator 2
Before unset
Destructing Mr. Unset
After unset
Before func
Constructing Mrs. Scopee
Destructing Mrs. Scopee
After func
Destructing Terminator 1
Exiting
Shutdown function
Destructing Terminator 2
Destructing Terminator 1
So we can see that the destructor is called when we explicitly unset the object, when it goes out of scope, and when the script ends.
The __destruct() magic function is executed when the object is deleted/destroyed (using unset). It is not called during shutdown of a script. When a PHP script finishes executing, it cleans up the memory, but it doesn't 'delete' objects as such, thus the __destruct() methods aren't called.
You may be thinking of the register_shutdown_function(), which is fired when your PHP script finishes executing.
function shutdown()
{
// code here
echo 'this will be called last';
}
register_shutdown_function('shutdown');
My understanding is that destructors are automatically called for any remaining objects when the script ends.
Looking though the manual page on constructors and destructors, it seems the only way to bypass destructors entirely is if you call exit() from the destructor of an object that is destroyed prior to the object in question.
Are you using exit() in any of your destructors? Are there even multiple objects in your script?
If it's not too much trouble, perhaps you could post the actual code in question rather than the sample code you have in your question now. Aside from the typo in your sample constructor, that code should call both the constuctor and destructor for your test object.
The __destruct method of a class is called once all references to the object are unset.
For example
$dummy = (object) new Class();
The destructor is automatically called if the dummy object is set to null or the script is exited.
unset($dummy); // or $dummy = null;
//exit(); //also possible
However, there are three crucial memory notes to calling the destructor method:
Firstly, the desctructor method should be a public method, not protected or private.
Secondly, refrain from using internal and circular references. For example:
class NewDemo
{
function __construct()
{
$this->foo = $this;
}
function __destruct()
{
// this method will never be called
// and cause memory leaks
// unset will not clear the internal reference
}
}
The following will not work either:
$a = new Class();
$b = new Class();
$a->pointer = $b;
$b->pointer = $a;
unset($a); // will not call destructor
unset($b); // will not call destructor
Thirdly, deciding whether destructors are called after output is sent. Using
gc_collect_cycles()
one can determine whether all destructors are called before sending data to user.
See http://php.net/manual/en/language.oop5.decon.php for the sources and elaborate explanations of magic destruct methods with examples.