43

In PHP5, is the __destruct() method guaranteed to be called for each object instance? Can exceptions in the program prevent this from happening?

0

6 Answers 6

45

It's also worth mentioning that, in the case of a subclass that has its own destructor, the parent destructor is not called automatically.

You have to explicitly call parent::__destruct() from the subclass __destruct() method if the parent class does any required cleanup.

2
  • 21
    I believe this is true only when the child class implements its own __destruct(), otherwise the parent __destruct() will be called.
    – Geoff
    Commented Sep 30, 2008 at 4:03
  • I don't think this answers the question, but it's a nice side note...
    – SteveExdia
    Commented Aug 10, 2022 at 16:12
37

The destructor will be called when the all references are freed, or when the script terminates. I assume this means when the script terminates properly. I would say that critical exceptions would not guarantee the destructor to be called.

The PHP documentation is a little bit thin, but it does say that Exceptions in the destructor will cause issues.

5
  • 1
    I can say, that Exceptions during the shutdown process may cause segmentation faults.
    – KingCrunch
    Commented Dec 30, 2011 at 23:16
  • 4
    Also, calling exit() inside a destructor which is already inside the exit() sequence can terminate the process without additional destructors being called. Commented May 29, 2013 at 15:17
  • 1
    Sorry, I couldn't imagine your example. Would you may elaborate? @JasonCohen
    – revo
    Commented Jun 18, 2016 at 11:01
  • 3
    If you need the destructor to be called even in case of php fatal error then register it as shutdown handler in constructor: public function __construct() { register_shutdown_function(array($this, '__destruct')); }. The cost of this solution is that the object reference (and the object itself) exists till the end of php script execution. Still, there are case then it is worth of it - e.g. huge tmp files removal in destructor.
    – Mojo
    Commented Jun 30, 2016 at 9:20
  • I don't know why this is lower-voted than the top answer, which isn't an answer, because this is the answer.
    – SteveExdia
    Commented Aug 10, 2022 at 16:13
14

In my experience destructors will be always called in PHP 5.3, but be warned that if some piece of code calls exit() or if a fatal error occurs, PHP will call destructors in "any" order (I think the actual order is order in memory or the order the memory was reserved for the objects. In practice, this order is almost always problematic). This is referred as "shutdown sequence" in PHP documentation.

PHP documentation of destructors says:

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 there are no other references to a particular object, or in any order during the shutdown sequence.

As a result if you have class X which holds a reference to Y, the destructor of X may be called AFTER the destructor of Y has already been called. Hopefully, the reference to Y was not that important... Officially this is not a bug because it has been documented.

However, it's very hard to workaround this issue because officially PHP provides no way to know if destructor is called normally (destructors are called in correct order) or destructors are called in "any" order where you cannot use data from referenced objects because those might have been already destroyed. One could workaround this lack of detection using debug_backtrace() and examining the stack. Lack of normal stack seems to imply "shutdown sequence" with PHP 5.3 but this, too, is undefined. If you have circular references, the destructors of those objects will not be called at all with PHP 5.2 or lesser and will be called in "any" order during "shutdown sequence" in PHP 5.3 or greater. For circular references, there does not exists a logically "correct" order so "any" order is good for those.

There are some exceptions (this is PHP after all):

  • if exit() is called in another destructor, any remaining destructors will not be called (http://php.net/manual/en/language.oop5.decon.php)
  • if FATAL error occurs anywhere (many possible causes, e.g. trying to throw an exception out from any other destructor could be one cause), any remaining destructors will not be called.

Of course, if the PHP engine hits segmentation fault or some other internal bug occurs, then all bets are off.

If you want to understand current implementation of "shutdown sequence", see https://stackoverflow.com/a/14101767. Note that this implementation may change in future PHP versions.

3
  • Rationale for PHP: "I am sticking to the current implementation because I think it is the best trade-off. --Andi". Source: mail-archive.com/[email protected]/msg06393.html Commented Nov 28, 2011 at 12:14
  • 3
    Basically there are two groups of coders: (1) coders using global variables in destructors and (2) coders that do not use global variables in destructors AND correctly marks up dependencies by using object references to dependant objects. Because PHP tries to target the bottom end it caters for (1) over (2). The group (2) is left with incorrectly working code and no official way to workaround the issue. Commented Nov 28, 2011 at 12:19
  • See also: stackoverflow.com/questions/2385047/… Commented May 7, 2013 at 11:52
10

There is a current bug with circular references that stops the destruct method being called implicitly. http://bugs.php.net/bug.php?id=33595 It should be fixed in 5.3

1
  • It was closed in 5.3.
    – robsch
    Commented Feb 13, 2017 at 15:43
8

Use a shutdown function if you want to go for sure: register_shutdown_function()

1
  • 2
    From the documentation: "Multiple calls to register_shutdown_function() can be made, and each will be called in the same order as they were registered. If you call exit() within one registered shutdown function, processing will stop completely and no other registered shutdown functions will be called." There's no guarantee that your shutdown function will be called if some other shutdown function runs before yours. Commented Sep 2, 2013 at 9:23
2

It's worth noting, that although destructors are likely to be called, there is no guarantee they will be called, as this tale from The Daily WTF illustrates:

“All right,” I sighed. As tempting as it was to ask him to describe the just-made-up concept of process marshaling, I decided to concede. And just at that moment, I thought of the perfect rebuttal. “But what if you just, say, pull the plug? A Finally block __destruct won’t execute when the computer is turned off!”

I expected the developers to jump back and chastise me for an “unreasonable scenario.” But instead, their faces turned bright white. They slowly turned to look at each other. It was clear that they made the same mistake that many before them had made: believing Try-Finally to be as infallible as things like database transactions.

“And… umm…” I said slowly, breaking the awkward silence, “that’s why… you should… never put critical… business transaction code in finally blocks.”

It's okay to assume that destructor will be called if you're just freeing resources or doing some logging, but they aren't safe to use for 'undo-ing' previous operations to try to make some code ACID compliant.

Not the answer you're looking for? Browse other questions tagged or ask your own question.