1

I am trying to implement a Server-Sent Event where the server will send the clients new messages.

The challenge here is that the server updated sessions and then echo their content.

The problem is that I will need to have that in one infinite loop.

Here is what I have done.

    while(true){


        session_start();
        header("Content-Type: text/event-stream" . PHP_EOL);
        header("Cache-Control: no-cache" . PHP_EOL);

        //configure the connection
        $conf = new ICWS\Config\Config($url, $stationName); 

        //create a new instance of icws
        $attrebutes = array('AccoRDI_mid','AccoRDI_account_id');
        $icws = new ICWS\Connection($conf, $attrebutes, true); 

        $messaging = new ICWS\Messaging($icws);

        $messaging->processMessages();
        session_write_close();

        $result = $messaging->getCallsQueue();

        echo 'event: getMessagingQueue' . PHP_EOL;
        echo 'data: ' . json_encode( $result) . PHP_EOL;    

        ob_end_flush();
        flush(); 

        usleep($sleepTime * 1000000);
    }

The problem here is that I get the following warning/notices

Warning: session_start(): Cannot send session cache limiter - headers already sent and 

Notice: ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush i

The reason that I need the session_start() in the loop is because I will need to unlock the session file immediately after reading it before the script sleeps. The idea is to

  1. Start/resume session
  2. Update session values
  3. Display the content of the session
  4. Unlock/release the session to allow other processes to use it
  5. Go back to step 1

What can I do to avoid the errors here?

Jaylen
  • 39,043
  • 40
  • 128
  • 221

2 Answers2

1

In your specific case... I would do without sessions. A session is just a serialized object into a temporary (well, garbage-collected) file. Who's to stop you from writing your own alternate session handler?

In its most basic form, leaving aside error checking and recovery, it's something like

function getAlternateSessionData() {
    $raw = file_get_contents(getAlternateSessionFileName());
    return unserialize($raw);
}

and the same with file_put_contents() to save the session. You do have to consider how to guard against concurrent access to the same session file by two different server processes. But you can send the session ID cookie at the beginning only, thus never actually "restarting" the session.

Another possibility would be to implement your alternate session using memory - if the server process does not terminate - or something like Redis (you can do that with ordinary sessions too, but those would send a session cache limiter).

You can also use session_set_save_handler() to override the file reading and writing functions of PHP with functions of your own, leaving all the session handling (cookies, etc.) intact. By handling yourself the session file management, you would be able to commit the session whenever you wanted. Still, you would need to consider carefully how to go with concurrent access to the session files, even if they are compatible with PHP's own session handler (if they aren't, you will have to include your session handler in every script accessing the "new" sessions).

As for ob_end_flush(), I believe that's because you never did call ob_start(). You can check the ob* level using ob_get_level() and flush it securely before the loop using ob_end_clean(), then instate a ob_start()/ob_end_flush() pair inside the loop.

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • Thank you for your answer. I like the idea of creating my own data saving mechanism. The only questions that come to my mind are, what will be the filename? Where would I store it? How to delete the file after is is not longer used? I mean if a user just close their browser, the server somehow will need to remove the file as garbage. – Jaylen Jun 22 '15 at 23:37
  • You will need garbage collection (as PHP itself does). Or a daemon; you could store session information using Redis, as I said, and let it do whatever it needs to be done. If you go with session_save_handler, garbage collection will be done automatically by PHP. – LSerni Jun 23 '15 at 07:02
  • Thank you. I wrote a class the handles the cache outside the sessions. thank you for your advise – Jaylen Jun 27 '15 at 18:26
1

Instead of starting the session for each loop, start it only if it hasn't already been started.

For PHP 5.4 and higher:

if (session_status() == PHP_SESSION_NONE) {
     session_start();
}

For PHP 5.3 and lower:

if(session_id() == '') {
    session_start();
}

You can't call ob_end_flush() without calling ob_start() first, but You can just use ob_flush() for what you are trying to do.

Lastly, are you doing this in a browser? The browser will give up on the page if it takes to long too respond, and looping indefinitely will eventually cause a time out. You'd be better off using ajax/javascript, or a meta refresh tag instead of looping on the PHP side.

Lin Meyer
  • 712
  • 5
  • 19