10

Ill start off by saying this works perfectly on my local machine, the js example below connects to stream.php and receives a continuous update of the servers current time every second.

index.php

var source = new EventSource("stream.php");

source.addEventListener('message', function(e) {
    console.log(e);
}, false);

source.addEventListener('open', function(e) {
    console.log(e);
}, false);

source.addEventListener('error', function(e) {
    if (e.readyState == EventSource.CLOSED) {
        console.log('closed');
    }
}, false);

stream.php

while(true)
{
    // Headers must be processed line by line.
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');

    // Set data line
    print "data: " . date( 'G:H:s', time() ) . PHP_EOL . PHP_EOL;

    // Toilet
    flush();

    // Wait one second.
    sleep(1);
}

I did expect a bit of a delay after uploading to the live dev. server. But there is a time delay of about 15 to 20 min. before I even see the first entry.

The connection does not drop. (Prob. been going 40 min. + now.) Is this just an Apache looping problem (means it time to look at web sockets) or is there something I can do to fix this?

CodeChap
  • 4,132
  • 6
  • 30
  • 40
  • I'm not sure this is even a valid method? The `server.php` is just running and running, and while I believe that the time spend sleeping is not counted towards the total run time (if it is counted as a 'system call', there is probably a limit to how long your scripts are allowed to run (default it is 30 seconds). So while the bulk of the time is spend in `sleep`, there is still a limit: http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time – Nanne Sep 06 '12 at 10:14
  • I am aware of the time limit, it wont be an issue. Its more about getting this first step rite. – CodeChap Sep 06 '12 at 10:19
  • Seems to me like you are using the wrong tools for the job... have a look at: http://nodejs.org/ – George Reith Sep 06 '12 at 11:00
  • 1
    Just a note: Ive found allot of examples in the wild where the guys are simply reconnecting to there server.php or stream.php file. Thats wrong. The connection should stay open to steam in data as it happens. If you keep reconnecting, your doing polling and that can be done via ajax, your defeating the purpose of server-sent-events. – CodeChap Sep 06 '12 at 14:25
  • @Derrick Connecting and reconnecting is bad when your server-side language is designed to handle running and running, but unfortunately PHP is *not*. See this link: http://software-gunslinger.tumblr.com/post/47131406821/php-is-meant-to-die ... A better way to go, potentially, is to keep a connection open for 10-30 seconds, then die and let the client reconnect. – Don McCurdy Sep 10 '13 at 21:31

5 Answers5

11

Server.php needs to be as follows:

stream.php

while(true)
{
    // Headers must be processed line by line.
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');

    // Set data line
    print "Event: server-time" . PHP_EOL;
    print "data: " . date( 'G:H:s', time() ) . PHP_EOL;
    print PHP_EOL;

    ob_end_flush();     // Strange behaviour, will not work
    flush();            // Unless both are called !

    // Wait one second.
    sleep(1);
}
CodeChap
  • 4,132
  • 6
  • 30
  • 40
  • 2
    Thanks for this. While the combination of `ob_flush()` and `flush()` didn't work for me, adding `ob_end_flush()` did. – JohnP Aug 14 '13 at 16:39
  • Strange that all the tutorials I've seen use `ob_flush` but the answer here using `ob_end_flush` is the one that works best. – Michael May 19 '15 at 09:39
  • Unfortunately, it's client-dependent, too. In my case, this hack didn't work (not even with PHP's output buffering anz zlib output disabled in the `.ini`). The only thing that did was transmitting enough padding junk to finally get the browsers writing to the page. In Chrome, that seemed to happen at 4K, FF waited even more (8K?) before the first message finally appeared. – Sz. Aug 28 '19 at 21:01
  • This helped for me but I'm still getting about a 10 second delay where the server flushes all the calls out to my browser at once. Is this a normal delay? Should PHP SSE be able to updated a browser each second, or is that asking too much? – Alec McKay Mar 19 '21 at 01:34
  • Using this I get `Cannot modify header information - headers already sent by ...` as a PHP warning. – Avatar Feb 18 '22 at 08:48
  • Maybe helpful: [php flush not working](https://stackoverflow.com/q/4706525/1066234) – Avatar Feb 18 '22 at 09:16
  • The flush did not work for me in the beginning because you need a certain amount of data so that PHP really flushes. See here: https://stackoverflow.com/a/4978642/1066234 "this is for the buffer achieve the minimum size in order to flush data" – Avatar Feb 18 '22 at 09:32
2

@Derrick, your suggested ob_end_flush(); line got me close, but in more complex PHP than hello world code, I was still getting unwanted reopens on my SSE connections (I still don't fully understand why ob_end_flush() was doing that to me). So here's the pattern I'm now using (otherwise identical to your stream.php). In English, I'm turning off PHP output buffering before entering my infinite loop:

// per http://www.php.net/manual/en/book.outcontrol.php:
//     Clean (erase) the output buffer and turn off output buffering
ob_end_clean();

// how long PHP script stays running/SSE connection stays open (seconds)
set_time_limit(60);
while (true) {

  // use @Derrick's header/send code here

  ob_flush();  // note I don't turn off output buffering here again
  flush();
  sleep(1);
}
dave_k_smith
  • 655
  • 1
  • 7
  • 22
  • You sure you don't have something outputting data higher up in the code somewhere? Any strange output would cause continuous reconnects. But if this works cool. – CodeChap Sep 18 '12 at 08:12
2

Sleep is blocking the SSE. I too had the same problem. I was advised to use event driven programming.

gzix
  • 271
  • 3
  • 20
0

just add, ob_flush(); before flush() function in stream.php

updated stream.php script is as below, observe ob_flush() function before flush() function.

while(true)
{
    // Headers must be processed line by line.
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');

    // Set data line
    print "data: " . date( 'G:H:s', time() ) . PHP_EOL . PHP_EOL;

    // Toilet
    **ob_flush();**
    flush();

    // Wait one second.
    sleep(1);
}
Shashidhar Gr
  • 420
  • 2
  • 6
  • 15
  • I think it would be more helpful for the OP and further vistors if you would extend the relevant code snippet with yours. – Reporter Jun 26 '14 at 08:54
0

you can set delay via echo retry, for example


header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); //to prevent caching of event data
header('Access-Control-Allow-Origin: *'); //allows stream access from current domain only

$time = date('h:i:s');
$id = time(); //to set id with current timestamp

echo "data:$time\n";
echo "retry:2000\n";  //2 seconds
echo "id=$id \n\n";

ob_flush();
flush();
dılo sürücü
  • 3,821
  • 1
  • 26
  • 28