views:

184

answers:

5

How do you run a long PHP script and keep sending updates to the browser via HTTP?

Something to do with output buffering but I don't know exactly how.

A: 

This looks like what you are after:

output buffering, PHP sends the output of your scripts to your web server as soon as it's ready - this might be line by line or code block by code block.

Output Buffering

The ob_start() function is used to create a new output buffer, and you can immediately start writing to it by printing out content as normal. Once you have a buffer open, there are two ways to close it: ob_end_flush() and ob_end_clean(), both of which end the buffer, but do so in slightly different ways. The former ends the buffer and sends all data to output, and the latter ends the buffer without sending it to output, effectively wiping out any information you saved in there. Every piece of text outputted while an output buffer is open is placed into that buffer as opposed to being sent to output. Consider the following script:

<?php
  ob_start();
  print "In first buffer!\n";
  ob_end_flush();
  ob_start();
  print "In second buffer!\n";
  ob_end_clean();
  ob_start();
  print "In third buffer!\n";
?>

That script will output "In first buffer" because the first text is placed into a buffer then flushed with ob_end_flush(). The "In second buffer" won't be printed out, though, because it's placed into a buffer which is cleaned using ob_end_clean() and not sent to output. Finally, the script will print out "In third buffer" because PHP automatically flushes open output buffers when it reaches the end of a script.

Kieran Andrews
Will the browser display flushed output without waiting for the whole HTML page to download? coz when I do this the browser waits for the whole page to finish ...
Jenko
+2  A: 

Output Buffering is thinking in the right direction, you start output buffering with ob_start() just like you would with sessions (session_start) somewhere in the top of your script, before any output is sent.

Then, you can use ob_flush and flush to keep flushing the output. For example, if you are in a foreach loop and at the end of each loop you want to output the new row and wait 1 second you would can do that.

But also look at set_time_limit, because otherwise people might experience a timeout after 30 seconds or so.

Another quick note, some browsers require a minimum number of bytes of output before they actually start showing it. I'm not sure what amound of bytes it was, I think it was around the 4000. Also, some browsers won't render certain elements (like tables) until they are closed. So flushing won't work there either.

CharlesLeaf
"some browsers require a minimum number of bytes" -- Firefox included? You mean this technique of sending updates via HTTP doesn't really update users as stuff is done on the server?
Jenko
I'm not sure which browsers behaved like that of the top of my head, I can tell you that in my experience and opinion this technique is not the best / most effective / reliable one. Let's just say that if you are able to use Javascript I would definitely pick that over this solution, personally.
CharlesLeaf
I have tested some code on Firefox, and it updates *without needing 4K bytes*, but you have to ensure that you end any open elements (within `<body>`) else it won't display, and you have to enclose outputted text in an element (such as a `<p>`)
Jenko
+1  A: 

You can also have a kind of background task, and an interface giving you the progress rate.

for instance, a page called job.php

<?php
    for ($i=0; $i<100; ++$i)
    {
       store($i);
       // long stuff
       sleep(42);
    }
?>

and progress.php

<?php
      return get($i);
?>

Then some ajax calls to progress.php?task=mytaskid and update the GUI. I have seen such method for a "big" file upload and found it great.

Edit: sorry, this doesn't exactly respond the initial question.

Aif
+1  A: 
<?php
# try this...
for (;;) {
  echo time() . '<br>';
  ob_flush(); # http://php.net/ob_flush
  flush(); # http://php.net/flush
  sleep(1); # http://php.net/sleep
}
?>
+1  A: 

I got it the page updating using simple HTTP outputting, to make it work:

  • Ensure that you close any open elements within <body> - else it won't display
  • Enclose outputted text in an element (such as a <p>)
  • Use output buffering & normal flushing
  • Tested on Firefox 3

Here is my code:

for ($nc=0; $nc<10; $nc++){

    // delay just to test
    sleep(1);

    // send message to browser
    ob_end_clean();
    ob_start();
    echo "<p>Update ".$nc."</p>";
    ob_end_flush();
    flush();
}
Jenko