Re: On register_shutdown_function: What might be the problem?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



That's a client-side issue then, because it is certainly sent.  Trying
your exact script:


<?php
ignore_user_abort(true);
header("Location: redirect2.html");
echo "foo\n"; flush();
for($i=0;$i<10;$i++) { echo $i; sleep(1); }
$fp = fopen("/tmp/foo.txt","a");
fputs($fp,$i);
fclose($fp);
?>

It's at http://lerdorf.com/red.php if you want to test it yourself.

3:53pm colo:/var/www/lerdorf.com> telnet localhost 80
Trying 127.0.0.1...
Connected to colo.
Escape character is '^]'.
GET /red.php HTTP/1.0

HTTP/1.1 302 Found
Date: Tue, 26 Jul 2005 22:53:40 GMT
Server: Apache/1.3.33 (Debian GNU/Linux) PHP/4.4.1-dev
X-Powered-By: PHP/4.4.1-dev
Location: redirect2.html
Connection: close
Content-Type: text/html; charset=utf-8

foo
<10 second delay here>
Connection closed by foreign host.

-Rasmus

Liang ZHONG wrote:
> Thank you for replying. Sorry for being long again.
> I tried your suggestion of this:
> 
> <?php
> ignore_user_abort(true);
> header("Location: redirect2.html");
> echo "foo\n"; flush();
> for($i=0;$i<10;$i++) { echo $i; sleep(1); }
> $fp = fopen("/tmp/foo.txt","a");
> fputs($fp,$i);
> fclose($fp);
> ?>
> 
> The browser did not get the redirect page until the new $i(10) has been
> appended to file foo.txt.
> 
> I also tried
> -----------------------------------------------
> ob_start();
> header("Location: http://liang.ns2user.info/php/y.html";);
> echo "foo\n";
> ob_end_flush();
> -----------------------------------------------
> 
> And does not work.
> 
> I think it might be the case of what you suggest:
> 
>> If the
>> client sticks around after seeing the redirect and doesn't redirect
>> until after the server has closed the connection, then there is no way
>> to force a close.
> 
> 
> So may I draw the conclusions:
> 1. Server in php can not actively close the connection to make the
> client see the redirect page immediately after the header has been set?
> 2. The connection is not closed even when the main php end and the
> shutdown function is running?
> 
> If above conclusions are not true, could be somewhere that might be
> wrong due to the apache-php configuration or what?
> 
> If those are true, I think I have to re-design the project flow control
> and it will take sometime while I have already finished the code
> assuming no above problem. Also I think this might result an awkward
> design for the project. (I mean jsp will be easier to achieve a
> relatively more elegant design).
> 
> I use several other program langurages for many years, but new to php,
> so I probably made some assumption base on my experiences of other
> langurages but not true. The project I am working now to add
> enhancements is written on php. I try to keep the main design of it. I
> did not expect it would be difficult to achieve this:
> 
> 1. write an xml page
> 2. let http client get this xml page in time
> 3. continue do something time consuming
> 
> now I have difficulty in 2 and 3. My client is normally perl
> LWP::UserAgent, and sometimes browser. I wonder how to let the client
> get the page in time before I can continue to finish the rest of the
> work. Otherwise the client will timeout and get no result.
> 
> Please help! Thank you.
> 
> Liang
> 
>>
>> I really didn't follow all that.  But this stuff is not that complex.
>> header() sets a header to be sent when output goes out.  header() does
>> not send an actual header at that point.  If you don't send any output,
>> then the headers won't go out until the script terminates.  If you have
>> any sort of output buffering enabled, then even if you think you are
>> sending something, you may only be buffering it, and in that case again
>> the headers won't go out until the request is done.
>>
>> Also, ignore_user_abort() controls whether or not your script will be
>> terminated when we are able to detect that the user has aborted.  A user
>> abort is defined as nobody being around to read the data we are sending
>> out.  If you don't send anything, we can't detect if the browser has
>> gone away.  Generally browsers will redirect as soon as they see a
>> Location: redirect header, but that could be client-speficic.  If the
>> client sticks around after seeing the redirect and doesn't redirect
>> until after the server has closed the connection, then there is no way
>> to force a close.  If it does redirect and close its end of the
>> connection, then if you set ignore_user_abort(false) your script will be
>> terminated as soon as it tries to send something further and your
>> shutdown function will be called at that point.
>>
>> -Rasmus
>>
>> Liang ZHONG wrote:
>> > Hi Rasmus,
>> >
>> > This may be a little bit long, sorry for taking your time.
>> >
>> > It still does not work as expected. I tried some experiment, and found
>> > that if I called some function or write some code line other then
>> > calling header(), the register_shutdown_function and other part of
>> codes
>> > work as expected . For example:
>> > <?php
>> > set_time_limit(5);
>> > function f(){
>> > set_time_limit(10);
>> > //doing something time consuming
>> > }
>> >
>> > some_function();
>> >
>> > ?>
>> > The time limit of 5 will be the limit of the some_function() and the 10
>> > will be the limit of function f() respectively.
>> >
>> > Code example:
>> >
>> >
>> -----------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > <?php
>> > set_time_limit(1);
>> > ignore_user_abort(true);
>> >
>> > function say_goodbye() {
>> >        $st = connection_status();
>> >        print "Status 1: ".$st."\n";
>> >        set_time_limit(10);
>> >        $st = connection_status();
>> >        print "Status 2: ".$st."\n";
>> >
>> >        $count=20000000;
>> >        for($i=0; $i<$count; $i++){    }
>> >            print "End!\n";
>> >            exec("touch /home/.nappy/liang/liang.ns2user.info/php/bbb");
>> >    }
>> >
>> >    register_shutdown_function("say_goodbye");
>> >    print "Sleeping...\n";
>> >
>> >    $count=10000000;
>> >    for($i=0; $i<$count; $i++){    }
>> >
>> >    print "Done!\n";
>> >
>> > ?>
>> >
>> ------------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 1 second exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>30</b><br />
>> > Status 1: 2
>> > Status 2: 2
>> > End!
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > if I change the time limit from 10 to 5 in function f()
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 1 second exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>30</b><br />
>> > Status 1: 2
>> > Status 2: 2
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 5 seconds exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>14</b><br />
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > Change both the time limit to 10 will result:
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > Done!
>> >
>> > Status 1: 0
>> > Status 2: 0
>> > End!
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> >
>> > But if the some_function is header() then the rule above does not work.
>> > It seems the function f()'s time limit also rule the header(). Only
>> > after the registered shutdown function finishes runing normally or by
>> > hit the expire time limit, will the header() return page to
>> browser/http
>> > user agent. Example code with suggestion from Rasmus as:
>> >
>> >
>> ------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > <?php
>> >
>> > set_time_limit(5);
>> >
>> > function f(){
>> > set_time_limit(100);
>> > $count=500000000;
>> > for($i=0; $i<$count; $i++){
>> >   //sit here and loop for a bit so we can have time to hit Stop...
>> >   echo "a \n"; flush();
>> > }
>> > echo "end";
>> > exec("touch /tmp/aaa");
>> > }
>> >
>> > register_shutdown_function('f');
>> >
>> > ignore_user_abort(true);
>> > header("Content-type: text/plain");
>> > header("Location: y.html");
>> >
>> > echo "foo\n"; flush();
>> > for($i=0;$i<10;$i++) { echo $i; sleep(1); }
>> > $fp = fopen("/tmp/foo.txt","a");
>> > fputs($fp,$i);
>> > fclose($fp);
>> > ?>
>> >
>> >
>> ------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > After the file /tmp/foo.txt has been created, before the file /tmp/aaa
>> > being created, the y.html will not get to the browser or perl program
>> > using LWP::UserAgent. I think it is no way to close the connection
>> > actively by the php program to deliver the page sooner to end the http
>> > request, even I add lines and make code like this does not work:
>> >
>> > ----------------------------------------------------------------
>> > header("Content-type: text/plain");
>> > header("Cache-Control: no-store, no-cache");
>> > header("Location: y.html");
>> > header("Connection: close");
>> > ----------------------------------------------------------------
>> >
>> > My project act as a broker for user agent to a library database server
>> > called z39.50. Upon the request from user agent, my program need to
>> > connect a z39.50 server, getting data back 1 by 1, transforming to
>> > sepcial xml format and sending back to the request party. When the data
>> > repository is huge (sometimes up to million records), I have to get
>> > partial data transform and send back to user agent (normally a piece of
>> > perl code, called harvester) with a resumption token. The harvester
>> will
>> > in a while loop send out another http request with the last resumption
>> > token and fetch data of next part, until finish all data fetching. The
>> > time to connect the database and do the query is a constant
>> overhead. So
>> > it is not a good design that program need to connect the database and
>> > query upon each request with or with out resumption token. So my design
>> > is to connect to database and query only when the first initial request
>> > comes, and reponse back with partical data using header() and continue
>> > getting back data from z39 server. Upon next request, the program will
>> > only need to make sure the data needed has already stored in the
>> > harddrive and transform them and send them back.
>> > Since the user harvester agent normally has a 180 second timeout, it is
>> > necessary to respond within that period of time.
>> >
>> > I really need your suggestion. Thank you very much again.
>> >
>> >
>> > Liang
>> >
>> >
>> >
>> >
>> >> Try somthing lik
>> >
>> >
>> > e this:
>> >
>> >>
>> >> <?php
>> >> ignore_user_abort(true);
>> >> header("Location: redirect2.html");
>> >> echo "foo\n"; flush();
>> >> for($i=0;$i<10;$i++) { echo $i; sleep(1); }
>> >> $fp = fopen("/tmp/foo.txt","a");
>> >> fputs($fp,$i);
>> >> fclose($fp);
>> >> ?>
>> >>
>> >>
>> >> Liang ZHONG wrote:
>> >> > Sorry, does not seem to work here. The code below takes minutes
>> to show
>> >> > up in browser.
>> >> >
>> >> > Any more suggestion?
>> >> >
>> >>
>> ---------------------------------------------------------------------------------------
>>
>> >>
>> >> >
>> >> > <?php
>> >> >
>> >> > set_time_limit(5);
>> >> >
>> >> > function f(){
>> >> > set_time_limit(100);
>> >> > $count=500000000;
>> >> > for($i=0; $i<$count; $i++){ }
>> >> > echo "end";
>> >> > exec("touch /home/.nappy/liang/liang.ns2user.info/php/aaa");
>> >> > }
>> >> >
>> >> > register_shutdown_function('f');
>> >> >
>> >> > ignore_user_abort(true);
>> >> > header("Content-type: text/plain");
>> >> > header("Location: y.html");
>> >> >
>> >> > $count=50000;
>> >> > for($i=0; $i<$count; $i++){ echo " \n";  }
>> >> > flush();
>> >> > ?>
>> >> >
>> >>
>> ---------------------------------------------------------------------------------------
>>
>> >>
>> >> >
>> >> >
>> >> > Liang
>> >> >
>> >> >>
>> >> >> If you don't flush some output after setting the header() then the
>> >> >> headers won't go out until the end of the request.  So do something
>> >> like:
>> >> >>
>> >> >> ignore_user_abort(true);
>> >> >> header("Location: http://whatever";);
>> >> >> echo "foo\n"; flush();
>> >> >>
>> >> >> Then whatever comes after this should run and the browser is long
>> >> gone.
>> >> >>
>> >> >> -Rasmus
>> >> >>
>> >> >>
>> >> >> Liang ZHONG wrote:
>> >> >> > I think I did not express myself clearly.
>> >> >> >
>> >> >> > What I want is to be able to redirect user an existing page (let
>> >> them
>> >> >> > get it immediately), and to close the connection actively, NOT
>> >> >> passively
>> >> >> > by user abort, at last, to run the function in background.
>> >> >> >
>> >> >> > But the redirecting using function header() with location has a
>> >> problem
>> >> >> > that header() always does return the page to user after the
>> entire
>> >> >> > program, including registered showdown function finish running,
>> >> >> which is
>> >> >> > against the will. I put a time consuming task into a function
>> that
>> >> >> > registered to be a shutdown function and hoping it runs after the
>> >> user
>> >> >> > has got the redirected page and the connection has been
>> closed. But
>> >> >> > experiements (using browsers, curl command line tool as well as
>> >> >> > perl::LWP code) show that the user got the redirected page only
>> >> after
>> >> >> > the shutdown function finished, which is against the
>> description of
>> >> >> > register_shutdown_function at php website.
>> >> >> >
>> >> >> > It seems only header() function use to redirect page has this
>> >> problem
>> >> >> > (not executed until register_shutdown_function finished) while
>> other
>> >> >> > functions like print()/echo(), exec() have not.
>> >> >> >
>> >> >> > The code looks like:
>> >> >> >
>> >> >>
>> >>
>> ---------------------------------------------------------------------------------------------
>>
>> >>
>> >> >>
>> >> >> >
>> >> >> > <?php
>> >> >> > set_time_limit(1);
>> >> >> >
>> >> >> > function f(){
>> >> >> > set_time_limit(20);
>> >> >> > $count=50000000;
>> >> >> > for($i=0; $i<$count; $i++){ }
>> >> >> > echo "end";
>> >> >> > exec("touch /home/.nappy/liang/liang.ns2user.info/php/aaa");
>> >> >> > }
>> >> >> >
>> >> >> > register_shutdown_function('f');
>> >> >> >
>> >> >> > header("Content-type: text/plain");
>> >> >> > header("Location: y.html");
>> >> >> > ?>
>> >> >> >
>> >> >>
>> >>
>> ---------------------------------------------------------------------------------------------
>>
>> >>
>> >> >>
>> >> >> >
>> >> >> >
>> >> >> > http client who sends the request to the php program will only
>> >> get the
>> >> >> > page back as response after function f finsihes (file aaa
>> created).
>> >> >> > Changing the $count will make a lot difference.
>> >> >> >
>> >> >> > My BIGGEST question is:
>> >> >> > How to make user get the redirect page immediately after the
>> >> >> header() is
>> >> >> > called, and not until function f() ends, while making sure
>> that the
>> >> >> > function f() will finally fully (and might slowly) execute?
>> >> >> >
>> >> >> > Thank you very much for kindly replying.
>> >> >> >
>> >> >> > With high respect,
>> >> >> >
>> >> >> > Liang
>> >> >> >
>> >> >> >
>> >> >> >>
>> >> >> >> Liang ZHONG wrote:
>> >> >> >> > My Question is:
>> >> >> >> > What is the correct way to keep the function running after I
>> >> >> >> redirect an
>> >> >> >> > existing page to http client (which I want the client get
>> >> >> immediately)
>> >> >> >> > and then immediately close the connection?
>> >> >> >>
>> >> >> >> ignore_user_abort(true);
>> >> >> >>
>> >> >> >> -Rasmus
>> >> >> >>
>> >> >> >> --
>> >> >> >> PHP General Mailing List (http://www.php.net/)
>> >> >> >> To unsubscribe, visit: http://www.php.net/unsub.php
>> >> >> >>
>> >> >> >
>> >> >>
>> >> >> --
>> >> >> PHP General Mailing List (http://www.php.net/)
>> >> >> To unsubscribe, visit: http://www.php.net/unsub.php
>> >> >>
>> >> >
>> >>
>> >
>>
> 

-- 
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux