12

I have a PHP class that creates a PNG image on the fly and sends it to browser. PHP manual says that I need to make sure that imagedestroy function is called at end to release the memory. Now, if I weren't using a class, I would have some code like this:

function shutdown_func() 
{
    global $img;
    if ($img)
        imagedestroy($img);
}
register_shutdown_function("shutdown_func");

However, I believe that appropriate place for my class would be to place a call to imagedestroy in class' destructor.

I failed to find out if destructors get called the same way shutdown functions does? For example, if execution stops when user presses the STOP button in browser.

Note: whatever you write in your answer, please point to some article or manual page (URL) that supports it.

2
  • I'd appreciate when you would not edit my answers to remove sections you find "irrelevant", thats disrespectful. Regarding garbage collection it was relevant, but suit yourself. Commented Oct 25, 2008 at 19:33
  • @Tomalak: The other part of your answer was relevant and spot-on, I even voted it up. Could you please place it back? Commented Oct 25, 2008 at 19:58

4 Answers 4

17

I just tested with Apache, PHP being used as Apache module. I created an endless loop like this:

<?php
class X
{
    function __destruct()
    {
        $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
        fputs($fp, "Destroyed\n");
        fclose($fp);
    }
};

$obj = new X();
while (true) {
    // do nothing
}
?>

Here's what I found out:

  • pressing STOP button in Firefox does not stop this script
  • If I shut down Apache, destructor does not get called
  • It stops when it reaches PHP max_execution_time and destuctor does not get called

However, doing this:

<?php
function shutdown_func() {
    $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
    fputs($fp, "Destroyed2\n");
    fclose($fp);
}
register_shutdown_function("shutdown_func");

while (true) {
    // do nothing
}
?>

shutdown_func gets called. So this means that class destuctor is not that good as shutdown functions.

Sign up to request clarification or add additional context in comments.

5 Comments

Can you add a link to anything in the php manual or otherwise that supports your test? That would be of great benefit to us all.
If you have a working setup, just copy/paste this examples I gave and try yourself. It it was explained in PHP manual or otherwise I wouldn't ask here at SO.
This behavior is not "by design" so it may change in future versions. If no output is send to the browser the stop button has no effect. "echo" is a function that can cause a fatal error (broken pipe). Thanks for your research but in the GD img case everybody should use the destructor method.
That's documented in the PHP Manual. Both, the registered shutdown function and destructors get called when the script ends. However, if the connection state changes to ABORTED (php has sensed user pressed the browsers stop button) and ignore_user_abort is not set to true, the registered shutdown function will be called not when the script ends, but when this was sensed. See Shutdown/Destructor and connection handling.
The fact, that pressing Stop in Firefox (client-side) does not stops execution of your script (sever-side) is so bloody obvious to me, that I must be missing some point. You wouldn't possible assume, that this could work that way, right? The same as shutting down Apache (killing everything) and reaching max_execution_time (critical condition, killing everything). How in the name of God could you expect, that in any of these situations PHP will be able to call your destructor? If it is completely and entirely killed at once in these circumstances. I must be missing something in your answer!
2

Based on the principle that you should finish what you start, I'd say the destructor is the correct place for the free call.

The destructor will be called when the object is disposed of, whereas a shutdown function will not be called until script execution finishes. As noted by Wolfie, these won't necessarily happen if you forcibly halt the server or the script, but at that time, the memory allocated by PHP will be freed anyway.

Also noted by Wolfie, PHP will free up script resources when the script closes, so if you're only instantiating one of these objects, then you probably wouldn't notice a massive difference. However, if you later do end up instantiating these things, or do so in a loop, then you probably don't want to have to worry about a sudden spike in memory usage, so for the sake of future sanity, I return to my original recommendation; put it in the destructor.

Comments

1

I recently had trouble with this as I was trying to handle destruction specifically for the case where the server experiences a timeout and I wanted to include class data in the error log. I would receive an error when referencing &$this (although I've seen it done in a few examples, possibly a version issue or a symfony side-effect), and the solution I came up with was fairly clean:

class MyClass
{
    protected $myVar;

    /**
     * constructor, registers shutdown handling
     */
    public function __construct()
    {
        $this->myVar = array();

        // workaround: set $self because $this fails
        $self = $this;
        // register for error logging in case of timeout
        $shutdown = function () use (&$self) {
            $self->shutdown();
        };
        register_shutdown_function($shutdown);
    }

    /**
     * handle shutdown events
     */
    public function shutdown()
    {
        $error = error_get_last();
        // if shutdown in error
        if ($error['type'] === E_ERROR) {
            // write contents to error log
            error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0);
        }
    }

    ...

Hope this helps someone!

Comments

0

I think one big thing that you have missed is that all the memory PHP has allocated during script execution is freed once the script terminates. Even if the user presses the stop-button, PHP processes the script until it is finished, gives it back to the HTTP daemon to be served to the visitor (or not, depending on how clever the daemon is).

So, explicitly freeing up memory at the end of script execution is a bit redundant. Some might argue that it would be a good thing to do, but it's still redundant.

But, on the topic of class destructors, they are called whenever the object is destroyed, either explicitly by unset() or at script completion/termination.

The recommendation of the developer explicitly freeing up the memory used in image manipulation is sure just to make absolutely sure not to have a memory leak, as bitmaps can be straining on the memory side of things (height * width * bit depth * 3 (+ 1 if you have an alpha channel))

To satisfy your Wikipedian needs:

5 Comments

AFAIU, memory is not allocated by PHP because it invokes GD to create images and GD does not have garbage collection.
I already read PHP manual, but it does not explicitly say whether destructor is called if script is terminated by web server.
also AFAIK, GD is just a library that is dynamically linked with PHP. This would imply that while GD knows how to do all the stuff, it's still PHP's memory and thus PHP's garbage collection.
@wolfie: no. I just tested and it leaks memory. Since PHP is Apache module, it gets returned to OS when Apache is restarted, which I don't do that often (any hosting company tries not to restart web server at all)
The situation may be different depending on whether it's a module or a cgi/fast-cgi setup.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.