register_shutdown_function

(PHP 3 >= 3.0.4, PHP 4, PHP 5)

register_shutdown_function --  Регистрирует функцию, которая выполняется по завершению работы скрипта

Описание

void register_shutdown_function ( callback function )

Регистрирует функцию function, которая выполнится после завершения работы скрипта.

Возможна регистрация нескольких подобных функций с помощью register_shutdown_function(), при этом функции будут выполняться в том порядке, в каком они были зарегистрированы. Если вы вызовете exit() в одной из зарегистрированных register_shutdown_function() функций, процесс будет остановлен окончательно и последующие зарегистрированные с помощью register_shutdown_function() функции не будут вызваны.

Зарегистрированные register_shutdown_function() функции будут вызваны после того, как запрос выполнен окончательно (включая отсылку любых буферов вывода), соответственно вы не сможете из этих функций вывести что-либо в броузер, используя echo() или print(), или возвратить содержимое какого-либо буфера вывода, используя ob_get_contents().

Замечание: Обычно неопределённые функции вызывают фатальные ошибки в PHP, но когда function, вызванная при помощи register_shutdown_function(), неопределена, уровень ошибки изменяется на E_WARNING. Также, из-за внутренних причин, эта ошибка будет ссылаться на Unknown() at line #0.

См. также auto_append_file, exit() и секцию connection handling.



register_shutdown_function
morphling at quick dot cz
30-Apr-2006 03:14
There is a note "Shutdown function is called during the script shutdown so headers are always already sent.", but my php 5.1 seems to act differently.
Example:
<?php
class Test {
  
   private
$running;
  
   public function
main() {
      
$this->running = true;
      
ob_start();
      
error_reporting(E_ALL);
      
register_shutdown_function(array($this, "clean_exit"));
       echo
"Hello";
      
// triggers E_ERROR
      
$fatal->error();
      
      
$this->running = false;   
   }

   public function
clean_exit() {
       if (
$this->running) {
          
header("Location: error.php");   
       }   
   }
}
$t = new Test();
$t->main();
?>
This example redirects you on error.php, this could be a simple way to handle E_ERROR.
michael at onlinecity dot dk
20-Apr-2006 01:07
Note that under Windows 2003/IIS6 and PHP5 5.1.2 ISAPI the register_shutdown_function executes under IUSR_, regardless of what user the script executes under.
This means that even if you use Application Pools and NETWORK SERVICE as user, shutdown functions will run under the default IIS user.
dweingart at pobox dot com
13-Mar-2006 08:53
I have discovered a change in behavior from PHP 5.0.4 to PHP 5.1.2 when using a shutdown function in conjunction with an output buffering callback.

In PHP 5.0.4 (and earlier versions I believe) the shutdown function is called after the output buffering callback.

In PHP 5.1.2 (not sure when the change occurred) the shutdown function is called before the output buffering callback.

Test code:
<?php
function ob_callback($buf) {
  
$buf .= '<li>' . __FUNCTION__ .'</li>';
   return
$buf;
}

function
shutdown_func() {
   echo
'<li>' . __FUNCTION__ .'</li>';
}

ob_start('ob_callback');
register_shutdown_function('shutdown_func');
echo
'<ol>';
?>

PHP 5.0.4:

1. ob_callback
2. shutdown_func

PHP 5.1.2:

1. shutdown_func
2. ob_callback
me at thomaskeller dot biz
11-Mar-2006 02:27
Well, it might be obvious, but one should remember that one cannot send any HTTP header in the shutdown callback.

Something like

<?php

function redirect()
{
    
header("Location: myuri.php");
}

register_shutdown_function("redirect");

// do something useful here

?>

doesn't work and PHP sends out the popular "headers already sent" warning.

I tried to set a redirection target somewhere in the script, but wanted to make sure that it was only set/executed at the very end of the script, since my custom redirect function also cleaned any output buffers at that point. Well, no luck here =)
kenneth dot kalmer at gmail dot com
27-Jan-2006 09:26
I performed two tests on the register_shutdown_function() to see under what conditions it was called, and if a can call a static method from a class. Here are the results:

<?php
/**
 * Tests the shutdown function being able to call a static methods
 */
class Shutdown
{
   public static function
Method ($mixed = 0)
   {
      
// we need absolute
      
$ap = dirname (__FILE__);
      
$mixed = time () . " - $mixed\n";
      
file_put_contents ("$ap/shutdown.log", $mixed, FILE_APPEND);
   }
}
// 3. Throw an exception
register_shutdown_function (array ('Shutdown', 'Method'), 'throw');
throw new
Exception ('bla bla');

// 2. Use the exit command
//register_shutdown_function (array ('Shutdown', 'Method'), 'exit');
//exit ('exiting here...')

// 1. Exit normally
//register_shutdown_function (array ('Shutdown', 'Method'));
?>

To test simply leave one of the three test lines uncommented and execute. Executing bottom-up yielded:

1138382480 - 0
1138382503 - exit
1138382564 - throw

HTH
sezer yalcin
26-Dec-2005 09:11
re:codeslinger at compsalot dot com

fork() is actually creating 2 processes from one. So there is no surprise register_shutdown_function will be executed per each process.

I think you have reported this as a bug and now in php 5.1.0 when you exit from child process, it does not execute it. Well, what are you going to do if you have something to register for forked process?

I hope php will be more stable near soon!

codeslinger at compsalot dot com
03-Feb-2005 09:22
Here is a nice little surprise to keep in mind...

If you register a shutdown function for your main program.  And then you fork() a child.

Guess What?
When the child exits it will run the code that was intended for the main program.  This can be a really bad thing  ;-)

Happily there is a simple work-around.  All you need to do is to create a global variable such as:

$IamaChild = [TRUE | FALSE];

and have your shutdown function check the value...
http://livejournal.com/~sinde1/
02-Dec-2005 04:49
If you want to do something with files in function, that registered in register_shutdown_function(), use ABSOLUTE paths to files instead of relative. Because when script processing is complete current working directory chages to ServerRoot (see httpd.conf)
Niels Ganser <php dot comment at depoll dot de>
22-Sep-2005 07:58
Just a quick note: from 5.0.5 on objects will be unloaded _before_ your shutdown function is called which means you can't use previously initiated objects (such as mysqli).

See bug 33772 for more information.
codeslinger at compsalot dot com
03-Feb-2005 09:22
Here is a nice little surprise to keep in mind...

If you register a shutdown function for your main program.  And then you fork() a child.

Guess What?
When the child exits it will run the code that was intended for the main program.  This can be a really bad thing  ;-)

Happily there is a simple work-around.  All you need to do is to create a global variable such as:

$IamaChild = [TRUE | FALSE];

and have your shutdown function check the value...
nospam at REM0VEME unclassified.de
26-Nov-2004 10:37
As long as the previous note still exists:
It may be easier to use dirname(__FILE__) with any relative filename in your script than setting the current directory and all those array operations. Use dirname(dirname(__FILE__)) for the parent directory.
php at kevin dot offwhite dot net
28-May-2004 02:14
Various commenters have described how this function doesn't work for time-consuming background processing, because the webpage doesn't close the connection.  The workaround using output buffering and header('Connection: close'); seems to work, but the solution I prefer is to put the background processing in a separate script which gets included as a transparent gif. 

<?php

 
//logger.php -- does the time-consuming log_it() function

 
function log_it()
  {
  
sleep(12); //simulate long processing
  
error_log('log_it() completed'); 
  }

 
error_reporting(0);
 
register_shutdown_function('log_it');
 
header('Cache-Control: no-cache');
 
header('Content-type: image/gif');

 
//the size of the unencoded transparent gif
 
header('Content-length: 85');

 
//1x1 pixel transparent gif
 
print base64_decode('R0lGODlhAQABALMAAAAAAIAAAACAA'.
                      
'ICAAAAAgIAAgACAgMDAwICAgP8AAA'.
                      
'D/AP//AAAA//8A/wD//wBiZCH5BAE'.
                      
'AAA8ALAAAAAABAAEAAAQC8EUAOw==');

 
flush();
  exit;
 

?>

@-----------Page you want to add background processing to ---------@
<html>
<body>
<h1>Hello World</h1>

<!-- Call logger.php to do background processing -->
<img src="logger.php" width="1" height="1" border="0" alt="" />

</body>
</html>
@------------------------------------------------------------------@

The only caveat is, of course, if the user has images turned off then the script won't be called.  Tested successfully with IE6, Firefox 0.8, and Opera 7.11
astrolox at lawyersonline dot co dot uk
18-Mar-2004 11:57
When using the register_shutdown_function command in php 4. The registered functions are called in the order that you register them.

This is important to note if you are doing database work using classes that register shutdown functions for themselves.

You must register the shutdown_functions in the order that you want things to shutdown. ( ie the database needs to shutdown last )

Example of what will not work but what you might expect to work :-
<?
class database {

       function
database() {
               echo
"connect to sql server -- (database :: constructor)<br>\n";
              
register_shutdown_function( array( &$this, "phpshutdown" ) );
              
$this->connected = 1;
       }
      
       function
do_sql( $sql ) {
               if (
$this->connected == 1 ) {
                       echo
"performing sql -- (database :: do_sql)<br>\n";
               } else {
                       echo
" ERROR -- CAN NOT PERFORM SQL -- NOT CONNECTED TO SERVER -- (database :: do_sql)<br>\n";
               }
       }
      
       function
phpshutdown() {
               echo
"close connection to sql server -- <b>(database :: shutdown)</b><br>\n";
              
$this->connected = 0;
       }     
}

class
table {
      
       function
table( &$database, $name ) {
              
$this->database =& $database;
              
$this->name = $name;
               echo
"read table data using database class -- name=$this->name -- (table :: constructor)<br>\n";
              
register_shutdown_function( array( &$this, "phpshutdown" ) );
              
$this->database->do_sql( "read table data" );
       }
      
       function
phpshutdown() {
               echo
"save changes to database -- name=$this->name -- <b>(table :: shutdown)</b><br>\n";
              
$this->database->do_sql( "save table data" );
       }
}

$db =& new database();

$shoppingcard =& new table( &$db, "cart " );
?>

Output of the above example is :-

connect to sql server -- (database :: constructor)
read table data using database class -- name=cart -- (table :: constructor)
performing sql -- (database :: do_sql)
close connection to sql server -- (database :: shutdown)
save changes to database -- name=cart -- (table :: shutdown)
ERROR -- CAN NOT PERFORM SQL -- NOT CONNECTED TO SERVER -- (database :: do_sql)
Jim Smith
15-Mar-2004 04:17
I was trying to figure out how to pass parameters to the register_shutdown_function() since you cannot register a function with parameters and passing through globals is not appropriate. E.g. what I was trying to do was  <? register_shutdown_function("anotherfunction('parameter')") ?>
Turns out, the trick is to use create_function() to create a "function" that calls the desired function with static parameters.

<?
$funtext
="mail('u@ho.com','mail test','sent after shutdown');";
register_shutdown_function(create_function('',$funtext));
?>

Here's another example showing in-line logging and a post-execution version:

Before: in-process logging
<?
function logit($message) {
  
$oF=fopen('TEST.log', 'a');
  
fwrite($oF,"$message\n");
  
fclose($oF);
  
sleep(5);  // so you can see the delay
}
print
"loging";
logit("traditional execution");
print
"logged";
exit;
?>
After:
<?
function logit($message) {
  
$forlater=create_function('',"loglater('$message');");
  
register_shutdown_function($forlater);
}
function
loglater($message) {
  
$oF=fopen('TEST.log', 'a');
  
fwrite($oF,"$message\n");
  
fclose($oF);
  
sleep(5);  // so you can see the delay
}
print
"loging";
logit("delayed execution");
print
"logged";
exit;
?>

In the 'before' example, the file is written (and the delay occurs) before the "logged" appears. In the 'after' example, the file is written after execution terminates.

Maybe it would be nice to add a parameter to the register_shutdown_function that does this automatically?
sts at mail dot xubion dot hu
15-Mar-2004 02:56
If you need the old (<4.1) behavior of register_shutdown_function you can achieve the same with "Connection: close" and "Content-Length: xxxx" headers if you know the exact size of the sent data (which can be easily caught with output buffering).
An example:
<?php
header
("Connection: close");
ob_start();
phpinfo();
$size=ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

The same will work with registered functions.
According to http spec, browsers should close the connection when they got the amount of data specified in Content-Length header. At least it works fine for me in IE6 and Opera7.
mmj
15-Mar-2004 02:45
In PHP version 4.1 and above, the function registered as a shutdown function can, in fact, output data to the browser, contrary to this manual entry.  The wording of the documentation has not yet been changed to reflect this new behaviour.

For more information about this new behaviour see
http://bugs.php.net/bug.php?id=20447 which has been categorized as a "Documentation problem".
James Wyse (James [at] tastyspoon.com)
12-Feb-2004 08:49
Just a note to say that if your function uses paths relative to the script you are running (ie. for reading files, "../dir/" etc) then your function may not work correctly when registered as a shutdown function. I'm not sure where the 'working dir' IS when running a shutdown function but I find it best to use absolute paths ("/home/www/dir/").
chrishmorris
27-Jan-2004 10:15
To end the connection, but continue background processing, other languages offer ways to close STDOUT. This is impossible in PHP, as other submissions point out. In fact, the STDOUT stream has no name in a PHP web script.

register_shutdown_function() is useful, and has nothing to do with that problem.
www dot php dot net at spam dot monnard dot org
01-Dec-2003 10:26
Response to => vbwebprofi at gmx dot de 22-Nov-2003 03:12

You have to do a changement on the object to see the difference between = and =&

Here is your code modified to see the difference.
The modification of the code are essentially the var $Value_changed and the function change().
I have tested it under RedHat 9 with PHP 4.2.2

--8<--- cls_TEST.php --------------------------------------------->8--
<?php
class TEST {
   var
$Value;
   var
$Value_changed;

  
// Constructor

  
function TEST($in_Value = 0){
      
// Register destructor
      
register_shutdown_function(array(&$this, '_TEST'));
      
      
$this->Value = $in_Value;
      
$this->Value_changed = $in_Value;
      
$this->_Log('TEST');
   }
  
  
// Destructor
  
function _TEST() {
      
$this->_Log('_TEST');
   }
  
  
// Private function
  
function _Log($in_Msg) {
      
$oF = fopen('/tmp/TEST.log', 'a');
      
fwrite($oF, sprintf("%s : %-5s = %d ; %d\n",
                          
date('Y-m-d H:i:s', time()),
                          
$in_Msg,
                          
$this->Value,
                          
$this->Value_changed));
      
fclose($oF);
   }
  
  
// Function change()
  
function change() {
      
$this->Value_changed++;
      
$this->_Log('change');
   }
}
?>
--8<--- test_TEST.php -------------------------------------------->8--
<?php
  
// include class TEST
  
include_once('cls_TEST.php');
  
   echo
"New...<br>\n";
  
$o1 =  new TEST(1);
  
$o2 =& new TEST(2);
  
# Strange declaration :
  
$o3 &= new TEST(3);
  
   echo
"Sleep...<br>\n";
  
sleep(5);
  
   echo
"Change...<br>\n";
  
$o1->change();
  
$o2->change();
  
# The following line doesn't work !
   #$o3->change();
  
  
echo "Sleep...<br>\n";
  
sleep(5);
  
   echo
"End.";
?>
--8<-------------------------------------------------------------->8--

It should produce a content like this in the file, specified in the _Log function :

2003-12-02 04:22:55 : TEST  = 1 ; 1
2003-12-02 04:22:55 : TEST  = 2 ; 2
2003-12-02 04:22:55 : TEST  = 3 ; 3
2003-12-02 04:23:01 : change = 1 ; 2
2003-12-02 04:23:01 : change = 2 ; 3
2003-12-02 04:23:06 : _TEST = 1 ; 1
2003-12-02 04:23:06 : _TEST = 2 ; 3
2003-12-02 04:23:06 : _TEST = 3 ; 3
vbwebprofi at gmx dot de
22-Nov-2003 09:12
Response to => php at spandex dot deleteme dot nildram dot co dot uk 28-Apr-2003 07:43

I have checked the methods to implement destructors in classes and
there is no requirement to use the syntax

$oX = &new Class();

See the code below and try, what fits your PHP-requirements. I have
tested with PHP 4.3.2 on Windows 2000.

--8<--- cls_TEST.php --------------------------------------------->8--
<?
 
// Class
 
class TEST {
   var
$Value;

  
// Constructor
  
function TEST($in_Value = 0){
    
// Register destructor
    
register_shutdown_function(array(&$this, '_TEST'));

    
$this->Value = $in_Value;
    
$this->_Log('TEST');
   }

  
// Destructor
  
function _TEST() {
    
$this->_Log('_TEST');
   }

  
// Private function
  
function _Log($in_Msg) {
    
$oF = fopen('E:/temp/TEST.log', 'a');
    
fwrite($oF, sprintf("%s : %-5s = %d\n",
                        
date('Y-m-d H:i:s', time()),
                        
$in_Msg,
                        
$this->Value));
    
fclose($oF);
   }
  }
?>
--8<--- test_TEST.php -------------------------------------------->8--
<?
 
// include class TEST
 
include_once('cls_TEST.php');

 
$o1 =  new TEST(1);
 
$o2 =& new TEST(2);
 
$o3 &= new TEST(3);
?>
--8<-------------------------------------------------------------->8--

It should produce a content like this in the file, specified in the
_Log function :

2003-11-22 15:10:43 : TEST  = 1
2003-11-22 15:10:43 : TEST  = 2
2003-11-22 15:10:43 : TEST  = 3
2003-11-22 15:10:43 : _TEST = 1
2003-11-22 15:10:43 : _TEST = 2
2003-11-22 15:10:43 : _TEST = 3

----------------------------------------------------------------------
http://www.vbwebprofi.de
gabe at websaviour dot com
21-Nov-2003 12:09
It should be noted that register_shutdown_function() does not guarantee that the connection will be closed before executing the function.  In particular on Apache 1.3.27 w/ PHP 4.3.0 on OS X as well as Apache 1.3.22 w/ PHP 4.1.2 on Red Hat I have discovered that Apache does not close the connection until after the registered shutdown function has completed execution.  I am not sure what the circumstances that cause this are, but there is a bug at http://bugs.php.net/bug.php?id=20447

This is important, because as far as I can tell, the documented functionality of register_shutdown_function() is the *ONLY* way to achieve anything resembling asynchronous processing with PHP in a web server environment.  In my mind this is a severe limitation, because if you have some processing intensive task that needs to be done and you want to do it in PHP, there is no way to have it occur without an accompanying wait in a browser window.  Sure you can use ignore_user_abort() to ensure that processing doesn't get cancelled by an impatient user (or impatient browser for that matter), but usability is still compromised.
phpmanual at NO_SPHAMnetebb dot com
15-Nov-2003 08:31
Given this code:

class CallbackClass {
   function CallbackFunction() {
       // refers to $this
   }

   function StaticFunction() {
       // doesn't refer to $this
   }
}

function NonClassFunction() {
}

there appear to be 3 ways to set a callback function in PHP (using register_shutdown_function() as an example):

1: register_shutdown_function('NonClassFunction');

2: register_shutdown_function(array('CallbackClass', 'StaticFunction'));

3: $o =& new CallbackClass();
   register_shutdown_function(array($o, 'CallbackFunction'));

The following may also prove useful:

class CallbackClass {
   function CallbackClass() {
       register_shutdown_function(array(&$this, 'CallbackFunction')); // the & is important
   }
  
   function CallbackFunction() {
       // refers to $this
   }
}
jules at sitepointAASASZZ dot com
01-Jul-2003 12:41
If your script exceeds the maximum execution time, and terminates thusly:

Fatal error: Maximum execution time of 20 seconds exceeded in - on line 12

The registered shutdown functions will still be executed.

I figured it was important that this be made clear!
php at spandex dot deleteme dot nildram dot co dot uk
28-Apr-2003 01:43
I've had immense trouble getting any of the examples of emulated destructors to work.  They always seemed to have a copy of the object just after initialisation.  Many people mention that the register_shutdown_function will take a copy of the object rather than a reference... and this can be cured with an ampersand.  If you look in the PEAR docs for the way they emulate destructors you'll find that you also need one before the "new" statement when you create an instance of your object.  There's an editors note above that mentions this too... but I thought I'd collect it all here in one example that really works.  Honest... I'm using it (PHP 4.3.1):-

class Object {
   var $somevar = "foo";

   function Object() {
       $somevar = "bar";
       register_shutdown_function(array(&$this, 'MyDestructor'));
   }

   function MyDestructor() {
       # Do useful destructor stuff here...
   }
}

# Now create the object as follows and then 'MyDestructor'
# will be called on shutdown and will be able to operate on
# the object as it ended up... not as it started!
$my_object =& new Object;
kwazy at php dot net
29-Jan-2003 07:53
One more note to add about register_shutdown_function and objects.

It is possible to self-register the object from the constructor, but even using the syntax:
register_shutdown_function(array(&$this,'shutdown'))

will only result in the function being called with the object in the "just initialized" state, any changes made to the object after construction will not be there when the script actually exits.

But if you use:
$obj = new object();
register_shutdown_function(array(&$obj,'shutdown'));

the object method 'shutdown' will be called with the current state of the object intact.

[Editor's note: The explaination for this behaviour is really simple.
"$obj = new object()" creates a copy of the object which you can refer with $this in the object's constructor.
To solve this "problem" you should use "$obj = &new object()" which assign the reference to the current object.]
23-Oct-2002 09:35
When using CLI ( and perhaps command line without CLI - I didn't test it) the shutdown function doesn't get called if the process gets a SIGINT or SIGTERM. only the natural exit of PHP calls the shutdown function.
To overcome the problem compile the command line interpreter with --enable-pcntl and add this code:

function sigint()
{
   exit;
}
pcntl_signal(SIGINT, 'sigint');
pcntl_signal(SIGTERM, 'sigint');

This way when the process recieves one of those signals, it quits normaly, and the shutdown function gets called.
Note: using the pcntl function in web server envoirment is considered problematic, so if you are writing a script that runs both from the command line and from the server, you should put some conditional code around that block that identifies wheater this is a command line envoirment or not.
adam at saki dot com dot au
20-Aug-2002 03:38
When using objects the syntax register_shutdown_function(array($object, 'function')) will take a copy of the object at the time of the call.  This means you cannot do this in the constructor and have it correctly destruct objects.  The alternative is to use register_shutdown_function(array(&$object, 'function')) where the ampersand passes the reference and not the copy.  This appears to work fine.
enygma at phpdeveloper dot org
16-Jul-2002 03:37
I figured I'd share this since I got it working on my box.... this lets you run the "bye" function if the page ends normally or they stop it.

<?php
set_time_limit
("0");
function
bye(){
  
//this is run when the page ends
  
if(connection_aborted()){
      
//this just means that the user hit the stop button or something
  
}
}
register_shutdown_function('bye');
$count=100000000000000;
for(
$i=0; $i<$count; $i++){
  
#sit here and loop for a bit so we can have time to hit Stop...
  
echo " \n";
}
echo
"end";
?>

I figured it might be useful to someone out there trying to run a function when the page ends (or is stopped).
saryon at unfix dot org
15-Jul-2002 04:49
But........if you turn the register_shutdown_functions
around to
register_shutdown_function('bye'); 
register_shutdown_function('thing');

it will give a message.
(seems logical, doesn't it? first do the first sd func,
then the second one)
priebe at mi-corporation dot com
14-Mar-2002 08:25
Note that register_shutdown_function() does not work under Apache on Windows platforms.  Your shutdown function will be called, but the connection will not close until the processing is complete.  Zend tells me that this is due to a difference between Apache for *nix and Apache for Windows.

I'm seeing similar behavior under IIS (using php4isapi).

<get_defined_functionsregister_tick_function>
 Last updated: Mon, 14 Nov 2005