Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   PHP (http://www.programmingforums.org/forum29.html)
-   -   register_shutdown_function() not behaving as expected (http://www.programmingforums.org/showthread.php?t=14965)

grimpirate Jan 17th, 2008 11:32 AM

register_shutdown_function() not behaving as expected
 
OK here's the code:
:

<?php
error_reporting(E_ALL);

function cleanup($filename){
        unlink($filename);
}

$filename = tempnam(getcwd(), 'tmp');

register_shutdown_function('cleanup', $filename);

set_time_limit(3);
sleep(4);
?>

Currently on CONNECTION_NORMAL and CONNECTION_TIMEOUT the script does in fact unlink($filename). However, when I press the STOP button on my browser, the CONNECTION_ABORTED state should launch the cleanup function and unlink the file, but it doesn't, what am I doing wrong?

Dameon Jan 17th, 2008 11:41 AM

Re: register_shutdown_function() not behaving as expected
 
Just use a real database. Your square wheel is a waste of time.

Sane Jan 17th, 2008 12:30 PM

Re: register_shutdown_function() not behaving as expected
 
I'm curious: have you tested your diagnosis in alternate browsers?

Dameon, I'm of the notion that there's no such thing as a waste of time, if your time consists of doing something that interests and challenges you. I imagine it's the same idea for grimpirate.

grimpirate Jan 17th, 2008 3:27 PM

Re: register_shutdown_function() not behaving as expected
 
@Dameon: As a PF veteran I would hope that you could post something relevant to the thread and not just discourage others. I figure the intent of the forum is to help people, not discourage them from what they're attempting to do (however useless it may seem to you).

@Sane: Hmm... no I haven't Sane. I've only tested on Opera. I'll see what happens on Firefox and IE.

grimpirate Jan 17th, 2008 4:31 PM

Re: register_shutdown_function() not behaving as expected
 
Tried it on IE and Firefox and it produced the same results. I actually added a line to the code
:

<?php
error_reporting(E_ALL);

ignore_user_abort(false);

function cleanup($filename){
        unlink($filename);
}

$filename = tempnam(getcwd(), 'tmp');

register_shutdown_function('cleanup', $filename);

set_time_limit(20);
sleep(30);
?>

And apparently switching between either true or false on the ignore_user_abort() function produces the same effect. The temporary file is being deleted but it's performing it due to a timeout not to a user abort. Does it happen to work on your machine Sane? I'm on a Windows XP machine with an Apache server which is what might be causing this behavior but I have no idea.

Sane Jan 17th, 2008 4:51 PM

Re: register_shutdown_function() not behaving as expected
 
I'm having permissions trouble with the server space I haven't yet been able to run PHP scripts on. I'll get back to you when I resolve that.

I don't know much about what you're trying to accomplish. I'm not sure if many do. But are you sure it's something standard? Seems unlikely that a user abort could trigger a function consistently.

I'm also wondering... maybe it only knows that the user aborted when it attempts to send a packet of data to the client, and sees that it's disconnected. When you've got the client hanging on a 30 second timeout, there's no data being transferred, right? So how would it know that the user clicked stop, unless there's some hidden communication going on? Maybe you could try outputting an infinite loop of numbers or something, and set a max running time on the script. Replace this with your sleep timeout.

grimpirate Jan 18th, 2008 9:34 AM

Re: register_shutdown_function() not behaving as expected
 
Attempted the following and it appears to have worked.
:

<?php
error_reporting(E_ALL);

ignore_user_abort(false);

function cleanup($filename){
        switch(connection_status()){
                case 1:
                case 2:
                case 3:
                unlink($filename);
                break;
        }
}

$filename = tempnam(getcwd(), 'tmp');

register_shutdown_function('cleanup', $filename);

set_time_limit(5);
do{
        echo 'foo';
}while(true);
?>

I'm going to see if I can write the output to a file to see if it's actually generating an CONNECTION_ABORTED condition.

grimpirate Jan 18th, 2008 9:58 AM

Re: register_shutdown_function() not behaving as expected
 
Well I couldn't output the connection_status() result to a file, buuuuuut I modded the code to
:

<?php
// Running on a Windows XP machine with an Apache server
error_reporting(E_ALL);

ignore_user_abort(false); // Does indeed affect behavior now

function cleanup($filename){
        switch(connection_status()){
                case 1:
                unlink($filename);
                case 2:
                case 3:
                break;
        }
}

$filename = tempnam(getcwd(), 'tmp');

register_shutdown_function('cleanup', $filename);

set_time_limit(5);
// Purposely causes a CONNECTION_TIMEOUT
do{
        echo 'foo';
}while(true);
?>

This unlinks the file if a CONNECTION_ABORT happens, but does not unlink the file if a CONNECTION_TIMEOUT or a CONNECTION_TIMEOUT & CONNECTION_ABORT happen simultaneously. Thanks a lot for the help Sane. It seems you were onto something about the browser output thing. My previous code was in fact deleting the file but I guess it deleted it after the machine returned from sleep().

grimpirate Jan 18th, 2008 10:20 AM

Re: register_shutdown_function() not behaving as expected
 
Since you were curious what I was trying to accomplish Sane here it is:
:

<?php
error_reporting(E_ALL);
ignore_user_abort(false); // Need to ensure behavior is false so in case of abort the mutex will be removed

/* Reference Material
 *
 * http://en.wikipedia.org/wiki/ACID
 *
 */
 
/*************/
/* Test Code */
/*************/
$mutex = new Mutex(getcwd() . '\tmp000.tmp', 10, 0755, true);
$mutex->acquireLock();

// Purposely causes a CONNECTION_TIMEOUT
do{
        echo 'foo';
}while(true);
/*************/

class Mutex {
        /* Private variables */
       
        var $filename;                        // The file to be locked
        var $timeout = 60;                // The timeout value of the lock
        var $permission = 0755;        // The permission value of the locked file
       
        /* Constructor */
       
        function Mutex($filename, $timeout = 1, $permission = null, $override = false){
                // Append '.lck' extension to filename for the locking mechanism
                $this->filename = $filename . '.lck';
                // Timeout should be some factor greater than the maximum script execution time
                $cfgtime = @get_cfg_var('max_execution_time');
                if($override === true){
                        if($timeout >= 1) $this->timeout = 2* $timeout;
                        set_time_limit($this->timeout / 2);
                }elseif($cfgtime !== false){
                        if($timeout >= 1) $this->timeout = $timeout * (2 * $cfgtime);
                }
                // Should some other permission value be necessary
                if(isset($permission)) $this->permission = $permission;
                register_shutdown_function(array($this, "lockAbort"));
        }
       
        /* Methods */
       
        function acquireLock(){
                // Create the locked file, the 'x' parameter is used to detect a preexisting lock
                $fp = @fopen($this->filename, 'x');
                // If an error occurs fail lock
                if($fp === false) return false;
                // If the permission set is unsuccessful fail lock
                if(!@chmod($this->filename, $this->permission)) return false;
                // If unable to write the timeout value fail lock
                if(false === @fwrite($fp, time() + intval($this->timeout))) return false;
                // If lock is successfully closed validate lock
                return fclose($fp);
        }
       
        function releaseLock(){
                // Delete the file with the extension '.lck'
                return @unlink($this->filename);
        }
       
        function lockTime(){
                // Retrieve the contents of the lock file
                $timeout = @file_get_contents($this->filename);
                // If no contents retrieved return error
                if($timeout === false) return false;
                // Return the timeout value
                return intval($timeout);
        }
       
        function lockFile(){
                return $this->filename;
        }
       
        function lockAbort(){
                switch(connection_status()){
                        case 0:
                        break;
                        default:
                        @unlink($this->filename);
                        break;
                }
        }
}
?>

Basically, this class now grants me the ability to lock files without having to use flock() or at least I feel confidently that it does.

Sane Jan 18th, 2008 3:13 PM

Re: register_shutdown_function() not behaving as expected
 
Ah, yes. So it only checked for disconnection after it was done sleeping. So I was close. :)

Very interesting. You should post this on a PHP guru website and see what they have to say about it. I'm sure you could get lots of great contributions, and people trying to bust holes in your algorithm.

What happens when multiple scripts try to request a lock for the same file at the same time?


All times are GMT -5. The time now is 3:27 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC