Are there situations in which this method would not be called?
I'm thinking to store a important variable into a persistent cache just before the cache object gets destroyed. This variable is used many times in the page so I wouldn't like to update the cache with it every time the variable changes...
Let's have a class:
class A {
public function __construct(){
echo "Construct\n";
}
public function __destruct(){
echo "Destruct\n";
}
}
And test code:
$test = new A();
die( "Dead\n"); // Will output Construct; dead; Destruct
$test = new A();
throw new Exception("Blah\n"); // Construct, Fatal error (no destruct)
$test = new A();
require_once( 'invalid_file.php'); // Construct, Fatal error (no destruct)
So basically: there are situations (fatal errors) when destructor won't be called.
Ah and this question has the same answer as this one: When will __destruct not be called in PHP? (+/-)
It is called as soon as there are no more references to that particular object, or during the shutdown sequence. The manual also states destructors are called when scripts are terminated with exit().
Aside from the issue pointed out by TimWolla, I am not aware of any problems with PHP destructors.
It seems there at least was a problem using Windows: https://github.com/WoltLab/WCF/blob/ff7e6ed381f2ccab7f51220f97087921133b2237/wcfsetup/install/files/lib/system/WCF.class.php#L122
I don't know whether this is still relevant.
Related
I've recently been redesigning an archaic PHP application, which had most of it's code in 1000+ line long PHP files for each page. I first started by refactoring much of the code into classes. I recently have been working on database connections, and began writing a class for it. I decided to throw $mysqli->close() into the destructor (I'm using the OO approach).
Unfortunately I've almost immediately ran in to problems with it. Instead of the MySQLi connection closing when the page finishes rendering (or when there are no more references to the DB object), it just immediately closes. I've tested this through writing a simple test:
$db = new DBConnect(); //My abstraction class
$db->getSQL()->query("SELECT 1"); //Query fails. Error message states connection closed.
My destructor looks like this:
public function __destruct() {
$this->mysqli->close();
}
My constructor looks like this:
public function __construct() {
$this->mysqli=new mysqli(\MainConfig::$database['host'], \MainConfig::$database['user'], \MainConfig::$database['pass'], \MainConfig::$database['db']);
if($this->mysqli->connect_error) {
die('Connect error (' . $this->mysqli->connect_errno . ') ' . $this->mysqli->connect_error); //This is not ever fired.
}
}
I know it's not some other bit of code closing the connection, since if I comment out the $this->mysqli->close() line, then the code works as expected. It seems to me that the destructor fires immediately which is not the desired behavior. Am I misunderstanding how they are intended to work?
I was able to reproduce the behavior you described with the code:
<?php
class DestructorTest {
public function getter() {
print "DestructorTest::getter() called\n";
return new CallableTest();
}
public function __destruct() {
print "DestructorTest destructor called\n";
}
}
class CallableTest {
public function method() {
print "CallableTest::method() called\n";
}
}
(new DestructorTest())->getter()->method();
which prints:
DestructorTest::getter() called
DestructorTest destructor called
CallableTest::method() called
The long and the short of it is: A destructor may be called as soon as there are no references to an object. This can even happen in the middle of a line -- in this case, for instance, after DestructorTest:getter() is called, the DestructorTest object is no longer reachable, so it is destroyed.
My advice:
You don't need to close MySQLi handles. They already have an internal destructor which will close them when they are garbage-collected.
In fact: avoid writing destructor methods in general. (This applies to many programming languages, not just PHP.) They are frequently not called when you expect them to be, and have a tendency to cause strange, hard-to-debug behavior.
If you're going to ignore this and write a destructor anyways, then you need to make sure that you don't inadvertently destroy something which you've "leaked" a reference to outside your class. In the case of your code, you've allowed code outside the class to get a reference to your MySQLi handle, but you're destroying that handle as soon as your object goes away -- even if the handle is still being used outside.
i have the following logic in my model:
if ( $switch_obj->connect() ) {
if ( $data = $switch_obj->showIntAll() ) {
$switch_obj->disconnect();
return $data;
}
else {
$switch_obj->disconnect();
throw new Exception('Empty Data Set');
}
}
else {
throw new Exception('Connection');
}
This switch_obj that's being called has logic in it's constructor and destructor to increment / decrement counters respectively. (saved in a class called testclass).
So each time an object of type testclass is instantiated, a counter is increased. And then when destroyed, it's decremented.
However, I've just discovered a scenario that I'm not handling.
Fatal error: Call to undefined method testclass::showIntAll() in
/var/www/myapp/application/models/test_model.php on line 215
It's clear that I'm calling a method that doesn't exist, which I will resolve. But my question is this: in creating this error, i can see that the counter has already been incremented ... but not decremented because once this error is thrown, it never returns to the destructor method in my class.
How would I program for these types of scenarios? Obviously, in production, I won't get getting errors because of missing methods in testclass... but in case I do get an unexpected error where the testclass constructor is called and then it bombs, I'm just wondering what the best way is to handle this.
You might achieve something with register_shutdown_function. Your constructor could register a clean-up function which would get called if an error would occur. You'd have to be careful not to call the clean-up code twice though (once from the destructor and once from this registered function.
Not a pretty solution, but it could work :)
As far as i can remember destructors are not called on fatal errors
I don't know why, but this code worked for me a month ago... maybe I upgraded the php but can't remember. Tried this with PHP 5.2.17 and 5.3.6
Why is it not possible to use a class object inside the callback of a ob_start function?
<?php
$f=new stdClass();
$f->title="awesome Title";
function callback($buffer)
{
global $f;
$buffer=str_replace("###TITLE###", $f->title, $buffer);
return $buffer;
}
ob_start("callback");
?>
This is the ###TITLE###
Output is:
PHP Notice: Trying to get property of non-object in /Users/qxxx/Sites/test/test.php on line 8
This is the
should be:
This is the awesome Title
This is because the output buffer is being implicitly flushed by the termination of the script.
At this point PHP has already destroyed unreferenced variables, so when it comes to execute your callback function, the variable $f does not exist in the global scope.
You can solve this by explicitly flushing the buffer before shutdown starts destroying objects, by placing the following line somewhere in your script.
register_shutdown_function('ob_end_flush');
Edit:
I'd like to add that even though this is currently the accepted answer that explains the "why", the solution provided here does not address the root cause of the issue; the fact that global is being used.
Many people will tell you that global is evil, without giving a reason why. Here you can see one of the reasons.
The answer provided by Jack gives a more "best practice" solution (using closures to maintain the variable reference), and should be considered as the proper way to avoid using global in new codebases.
The reason for this has been outlined well by Leigh. Using a closure would work better in this case:
ob_start(function($b) use ($f) {
return str_replace('###TITLE###', $f->title, $b);
});
This is because the closure will keep the reference to $f alive by the end of the script so that it won't get garbage collected before running the callback function.
From the php manual page of ob_start, and a bug report I learned that, since 5.2, all objects are destroyed #ob_start
This function's behaviour has been changed in php 5.2.0:
<?
global $AP;
$AP = new ap;
ob_start("ob_end");
function ob_end()
{
global $AP;
$r = $AP->test();
return $r;
}
class ap
{
function test()
{
return "debug";
}
}
?>
In older versions it shows: "debug".
But latest php version causes error: PHP Fatal error: Call to a member function test() on > a non-object.
And this is not a bug: http://bugs.php.net/bug.php?id=40104
from the man pages
In a PHP app, would it be a bad idea to run my saving code during an object's destructor? I ask because if it's ok then I could add a save() call in the destructor of the parent Model class and save myself the trouble of remembering anywhere else.
I know that doing this does work, as I have an entire app (albeit a poorly written one) running on it. But are there good reasons not to do it?
The destructor is not guaranteed to be called in any order. What happens if your page is unloading and all the objects instructors start to get called. you never know if the database object you need to use is still valid, or if it has been unloaded.
IMO, adding such functionality into the destructor is not the best choice. The reason, one very important to me, is increased code complexity and reduced readability. A third person new to the project will end-up spending quite a bit of time figuring out whats been happening.
Having said that, whether it is theoretically good or bad, its down to the programming logic being employed. If the class in question would be extended at a latter stage, then the save() in your destructor might give you some grief; again depends on what you are trying to achieve.
Actually, PHP will try to destroy objects in the right order, so it is quite safe (considering if you're trying to save something, it means you are still hodling a reference to it). What you need to be aware of is that throwing an exception during a destructor will cause a fatal error even if there's a catch. You can play with examples, and it's not easy to make PHP fail with normal referencing, here's a simple way to make PHP crazy with destructors so it doesn't know how to end them, but as I said, it's not something you normally find in your code:
<?php
class A
{
public $b;
function eco()
{
echo 'AAA';
}
function __destruct()
{
$b->eco();
}
}
class B
{
public $a;
function eco()
{
echo 'BBB';
}
function __destruct()
{
$a->eco();
}
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;
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.