2

I'm trying to figure out why this loop doesn't return anything to the browser:

while(1) {
    echo "hello";
    flush();
    sleep(1);
}

I'm expecting it to return "hello" to the browser every second... am I wrong? Right now the page just seems to hang.

mootymoots
  • 4,545
  • 9
  • 46
  • 74
  • your HTTP server is buffering the output – galchen Mar 08 '13 at 20:36
  • isnt that what flush() is for? – mootymoots Mar 08 '13 at 20:38
  • sort of, read http://php.net/manual/en/function.flush.php – galchen Mar 08 '13 at 20:40
  • @mootymoots: php has its own buffers which it can flush anytime. but the webserver can also do buffering, and php has very little/no control over that. there could be further buffering upstream as well (server network stack, reverse proxy system, etc...) and php will have absolutely no way to affect that. – Marc B Mar 08 '13 at 20:43

8 Answers8

3

PHP only outputs after execution has finished. so all you are doing where is generating a new hello every milisecond, and since you never exit the loop, you never see the output.

bizzehdee
  • 20,289
  • 11
  • 46
  • 76
2

To correct my answer and make you to understand better, and for the AJAX lovers... you need and extra flush there.. the 'ob_' one:

<?php
while( 1 ):
    echo "hello";
    ob_flush( ); flush();
    sleep( 1 );
endwhile;

This is the 'trick' for everyone who need to know ;)

valentin
  • 21
  • 1
  • This is not a good answer because it does not work reliably (if at all) as documented [here](http://stackoverflow.com/questions/5770917) and in the [comments of ob_flush()](http://www.php.net/manual/en/function.ob-flush.php#allnotes). AJAX is the only way to achieve what the question asked reliably and consistently across all browsers. AND without having to tinker with server settings. – mastaBlasta Mar 22 '13 at 15:17
0

The browser won't display anything until the entire page is received. PHP is not capable of what you're trying to accomplish.

In Javascript, this is pretty simple.

<script>
    window.setInterval(function(){document.innerHTML += "<br> Hello"}, 1000)
</script>
dieki
  • 2,435
  • 5
  • 23
  • 29
  • Capable only if nothing is buffering output along the rest of the pipeline, and browser and web servers almost always buffer output before displaying it. – dieki Mar 08 '13 at 20:40
  • More importantly, in what real-world scenario would you even want this functionality? – dieki Mar 08 '13 at 20:41
  • I was referring to comet examples, and they use this: e.g.: http://www.zeitoun.net/articles/comet_and_php/start – mootymoots Mar 08 '13 at 20:44
  • In read world scenario yes, but the problem is that the HTTP server is normally not configured for it. NodeJS is a great tool for these sorts of things, but it can be done with PHP. You can actually create a simple server using PHP for these sort of things, but I'd go with node for interactive content. – galchen Mar 08 '13 at 20:47
  • and @snostorm you are absolutely right, too many things in the middle so in normally PHP websites you wouldn't need/want this functionality. – galchen Mar 08 '13 at 20:48
  • The comet examples implement a technique called HTTP Streaming, which is difficult to implement with PHP. However, longpolling is much simpler and achieves approximately the same latency. http://www.xphp.info/php-tutorial/long-polling/ – dieki Mar 08 '13 at 21:02
0

You should realise that PHP is a scripting language in the sense that it returns the output only after completing the script. EDIT: Or after output buffers are filled, thanks @Marc B. Regardless I would say it is wiser to use JS for this or if you really need your server, use AJAX requests.

Perhaps you should consider using Javascript? That will allow you to add content every second (do keep in mind that JS is run at the clientside though, so you might not want to make your operations all that expansive then.)

Alternatively you could consider using AJAX requests through for instance JQuery, but that might be outside the scope of this question...

MrHug
  • 1,315
  • 10
  • 27
  • Not quite right. PHP can/will send output to clients as need be. But only when the script is completed and/or output buffers are filled. it is quite possible to have a php script send infinite data and that WILL show up on the client side fairly quickly. – Marc B Mar 08 '13 at 20:44
  • You're quite right, thanks I fixed my answer to reflect this comment :) – MrHug Mar 08 '13 at 20:48
0

Maybe is not to late to answer but if you want to flush every second here I give you a sample:

<?php
echo "Flushing every second ...\n";
flush( );
$seconds = array (0,1,2,3,4,5,6,7,8,9);

foreach ($seconds as $second):
    echo $second . "\n";
    sleep( 1 );
    @ob_flush( ); flush( );
endforeach;
echo 'I flashed 10 second :P';
0

Valentin gave you the right answer (upvote him/accept his answer!), but didn't explain why. Here's the explanation:

Output buffering

PHP doesn't output to the browser immediately, it waits to have some amount of content to send to the browser (probably it sends in chunks of 1024, 2048 or 4096 bytes), or when the execution ends. Calling flush() makes PHP send the data, but there is more than one layer of buffering. One is the internal buffering (what I've just commented), but you can add more layers of buffering. Suppose this code:

<?php
echo "hi";
setcookie('mycookie', 'somevalue');
?>

The setcookie() function sends an http header to the browser, but it can't do it because in HTTP, the server (or the client, it is the same both ways) must send first all headers, a blank line, and then the contents. As you see, you are outputting some content (hi) before the header, so it fails (because the internal buffering follows the same order of execution).

You can add another layer of output buffering, using the php functions ob_*(). With ob buffering it only buffers content output, not HTTP headers. And you can use them to get the output of functions that directly output to the browser, like var_dump(). Also, you can nest layers of ob:

<?php
// start first level of output buffering
ob_start();
echo "nesting at level ", ob_get_level(), "<br />\n"; // prints level 1
echo "hi<br />";
ob_start();
echo "nesting at level ", ob_get_level(), "<br />\n"; // prints level 2
var_dump($_POST);
$post_dump = ob_get_clean();

// this will print level 1, because ob_get_clean has finished one level.
echo "nesting at level ", ob_get_level(), "<br />\n";

echo "The output of var_dump(\$_POST) is $post_dump<br />\n";

// in spite of being inside a layer of output_buffering, this will work
setcookie('mycookie', 'somevalue');

// flush the current buffer and delete it (will be done automatically at the
// end of the script if not called explicitly)
ob_end_flush();

Probably your PHP server has output_buffering enabled by default. See the configuration variables to turn it off/on by default.

Community
  • 1
  • 1
Carlos Campderrós
  • 22,354
  • 11
  • 51
  • 57
0

Ok, Carlos criticize me because I didn't explained my answer but also his answer is to vague... with cookies, layers.. POST, ob_levels... :OO to much info with no real point about the real question of the user but I will tell you why your code is not working. Because you have set in the php.ini the output buffering something like:

output_buffering = On

or

output_buffering = 4096 (default setting on most distributions)

Thats why you need the extra 'ob_flush( )', to get rid of any garbage output..

so... To make your code work you have 2 options:

1). set output_buffering = 0 or Off (if you have access to the php.ini)
2). ob_flush many times as layers of buffering you have

If you don't know how many layers you have you can do something like:

while (@ob_end_clean( ));

and clean every garbage you can have, and then your code will work just fine.. Complete snipp:

<?php
while (@ob_end_clean( ));
while(1) {
    echo "hello";
    flush();
    sleep(1);
}

Cya..

-1

Adding to all the other answers,

To do asynchronous Server push to clients you'll need to use WebSockets. It's a vast subject and not fully standardized, but there are certainly ways of doing it. If you are interested search for PHP Websockets.

mastaBlasta
  • 5,700
  • 1
  • 24
  • 26