- Server push technologies
- Where's the demo?
- Browser support aka does it work in IE?
- So should we drop it until IE and Android support it?
- More information
- Version française
Server push technologies ¶
There are several methods and techniques that come handy in the case you need to call an external resource periodically or if you are waiting for a server push, but I present here an easy and straightforward one using HTML5's Server-Sent Events (SSE).
Let's say you have an internal messaging system in your webapp, and you want to display messages dynamically and real-time to the relevant recipients.
Where's the demo? ¶
No demo :-) But here are really easy steps to get it, and get it done.
Table structure ¶
Let's keep things simple, we'll have a standard User model that is not represented here, and a Message model based on the following table structure:
[sql]
CREATE TABLE `Message` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`toUserId` int unsigned NOT NULL,
`message` text NOT NULL,
`new` tinyint NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `toUserId` (`toUserId`)
);
View ¶
Say you want messages to show in any part of the webapp, so you can add an empty div to your main layout and a simple jQuery snippet firing the SSE call, like this:
<?php
Yii::app()->clientScript->registerScript('message-update', '
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("' . CController::createUrl('site/getMessage') . '");
source.onmessage = function(event) {
$("#message").prepend(event.data).fadeIn(); // We want to display new messages above the stack
};
}
else {
$("#message").replaceWith("<div class=\"flash-notice\">Sorry, your browser doesn\'t support SSE.<br>Hint: get a real one :-)</div>"); // Don\'t be desperate, we\'ll see what we can do for you at the end of the wiki
}
', CClientScript::POS_READY);
?>
<div id="message"></div>
Controller (SiteController.php) ¶
The app logic will be in our Site controller. The getMessage method will check at a defined interval (see below) whether there is any new message, and return a ready-to-display block that will be prepended to our message div.
So here's our controller method. Notice that you need to add the relevant access rule(s) for that method.
<?php
class SiteController extends Controller
{
…
/* Add access rules if needed for getMessage */
…
public function actionGetMessage()
{
$messageList = Message::model()->findAll(array(
'condition' => 'toUserId = :myId AND new = 1',
'order' => 'id DESC',
'params' => array(':myId' => Yii::app()->user->_id)
));
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "retry: 10000\n"; // Optional. We tell the browser to retry after 10 seconds
if(count($messageList)) {
foreach($messageList as $key => $message) {
echo "data: <p>" . $message->message . "</p>\n";
}
//Now we run a bulk update query in order to flag the just retrieved messages (new = 0)
$sql = 'UPDATE Message
SET new = 0
WHERE toUserId = ' . Yii::app()->user->_id;
$command = Yii::app()->db->createCommand($sql);
$command->execute();
}
flush();
}
…
}
?>
As you can notice, it's not real real-time, but a regular check at 10 second intervals. That is an optional parameter that falls back to the browser default if not specified.
Of course, the Message table update should be more accurate: with the above simplistic code, some messages could have been posted to the user during the method execution, so they would be flagged —wrongly— whatsoever.
Browser support aka does it work in IE? ¶
Internet Explorer and Android browser (all versions) don't support Server-Sent Events out of the box. Neither do older versions of Firefox (< 6), Chrome (< 6), Safari (< 5), iOS Safari (< 4), or Opera (< 11).
So should we drop it until IE and Android support it? ¶
It's up to you. There's a polyfill that gives SSE/EventSource support to IE 8+ and Firefox 3.5+.
More information ¶
Read more about SSE and Websockets and other techniques:
- HTML5 and Server-Sent Events
- WebSockets vs Server-Sent Events vs Long-polling
- Stream Updates with Server-Sent Events - aka Server-Sent Events vs. Websockets
Websockets
Why do you work not with websockets?
Sure, a good implementation is with node.js as server - but in JavaScript and not in PHP - so its possible to implement the server funktionality in PHP
see http://it-republik.de/php/artikel/WebSocket-Implementierung-mit-PHP-3816.html (german)
or http://code.google.com/p/phpwebsocket/
RE: Websockets
Thanks for your feedback. Maybe you missed the last two lines of the wiki ("More information"):
missed lines
... no, I read the "More Info" and I know the first article (Dimitry Sheiko)
but what is your reason to work with SSE? I dont see the benefit from long-polling to SSE...
RE: Websockets
@nitropenta It's the first push technology I had —recently— the opportunity to implement. I found it really easy and straightforward, and moreover, it serves the purpose, doesn't it? After a post in the forum, I imagined there could be a little wiki for it :)
For Websockets, I've also read about them, and thanks for the Google Code link. It looks interesting. I should try them when I have the chance, especially for bilateral stuff.
Websockets & PHP
At one of my projects I realize a multipe choice quiz with node.js and jqTouch - that works very good. I operate about 100 iPads for the quiz and PowerPoint at "operating panel" to start and stop the Q&A plus show a bar-chart for the results.
The disadvantage of this is a pure work at javascript ;-) and not PHP
I hope the follow links can you help for PHP:
Not worked
i tried this but not worked.
Re: Not worked
@sirin_ibin What exactly have you tried and has not worked?
Problem implementing this!
Spent alot of time trying to figure this out but haven't been successful.
JS code:
Yii::app()->clientScript->registerScript('message-update', ' if(typeof(EventSource) !== "undefined") { var source = new EventSource("' . CController::createUrl('site/getMessage') . '"); source.onmessage = function(event) { console.log(event.data); $("#message").prepend(event.data).fadeIn(); // We want to display new messages above the stack }; } else { $("#message").replaceWith("<div class=\"flash-notice\">Sorry, your browser doesn\'t support SSE.<br>Hint: get a real one :-)</div>"); // Don\'t be desperate, we\'ll see what we can do for you at the end of the wiki } ', CClientScript::POS_READY);
And the function
public function actionGetMessage() { header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); echo "retry: 10000\n"; echo "data: A new message\n"; flush(); }
The problem is that I'm not getting any response back whatsoever. This should print to the chrome console but its empty.
Though request header seems fine.
RE: Problem implementing this!
@sfsultan: Well your code looks ok to me… Which Chrome version are you using?
Also, are you sure your getMessage action is allowed in your controller's accessRules?
On a side note, have you included the #message div in your view?
~~~
[html]
~~~
I'm not sure it would break anything though if absent.
RE: RE: Problem implementing this!
Chrome version is 21, I am not using access control and there is that div to be populated by incoming messages.
Also, I set a break point in the "onmessage" callback but the JS doesn't break at it, meaning that "onmessage" event is never raised! :O
can i control it
can I use it to send mesage to a specific user(s)
or it only can broadcast the messages ???
Not working
Browser gets data by ajax but response is null or no response,
echo "data: 'data'"; in actionGetMessage not writing anything to browser.
RE: can i control it
@diaa
It's up to you, but the wiki shows how to send specific messages to specific users
RE: Not working
@Firebreaker
What browser / version are you using?
Are you seing the calls being made in Firebug at your time interval? Do they succeed? (Response code 200)
PS You won't see the server-returned data in Firebug, you should see only the calls and their reponse code and headers.
Got it. Remember \n\n
OK, here is the thing. What you were posting was good. But not fully correctly. I read HTML5 SSE articles and found out that to complete the SSE from server-side for 1 call it must end with with double '\n' e.i '\n\n'. Please update your post, before flush(); put echo '\n\n'; to end the request. Thanks for tutorial though. It helped me a lot to figure this out.
Aren't you really cheating?
Let's be honest: this is not a true SSE implementation because it is not the server that decides when to push the message, it's the retry of the EventSource object which is experiencing an error at the end of the stream and therefore re-sending its http request.
A true implementation would have a daemon process running on the server that sent events ONLY initiated on its own internal event context and not as the result of network traffic from the client every ten seconds. You might as well just use setInterval and an AJAX POST.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.