Live stream entre PHP et JavaScript
Nous voulons ici communiquer un flux du serveur vers le client, la meilleure solution que j’ai trouvé pour ça, c’est l’EventSource, c’est une classe javascript qui utilise le protocole Event Stream.
Ce protocole utilise un format statique assez simple, il est reconnu par la majorité des grands navigateurs et il permet d’envoyer continuellement des données au client sous forme d’événement. On en entend peu parler sur l’internet français, c’est dommage, c’est facile à utiliser et vraiment pratique. Nous allons voir comment l’utiliser avec PHP et Javascript.
L’Event Stream
Ces événements peuvent posséder 4 propriétés, nous n’utiliserons que les champs type et data ici, si vous le souhaitez, vous pouvez retrouver toutes les propriétés d’un event sur mozilla.org.
Le champs data est le plus important, il peut fonctionner tout seul, il contient les données à envoyer.
Voici un exemple simple:
data: Des données\n\n
Sur plusieurs lignes:
data: Des données\n
data: sur plusieurs lignes\n\n
Avec un type spécifié:
type: status\n
data: Nouveau statut\n\n
Vous pouvez ainsi remarquer que pour fermer un événement, on utiliser un double \n alors que pour terminer une ligne, on n’en met qu’un.
Le champs type permet de faire varier le type d’événement envoyé au navigateur et ainsi, on pourra envoyer des événements sous des formats totalement différents sans aucun soucis. Par défaut, le type est « message ».
Son implémentation en PHP
On veut à présent permettre à un script PHP accéder par une page web de diffuser un flux Event Stream en continu avec pour seule condition d’arrêt la déconnexion de l’utilisateur.
On commence par configurer PHP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
ignore_user_abort(true);// Ignorer la déconnexion de l'utilisateur set_time_limit(0);// Aucune limite d'exécution ini_set('output_buffering', 0);// Désactive le buffer de sortie apache_setenv('no-gzip', 1);// Désactive la compression ini_set('zlib.output_compression', 0);// Désactive la compression ini_set('implicit_flush', 1);// Flush dès que possible session_write_close();// Déverrouille la session // Vide tous les buffers actuels while( ob_get_level() && ob_end_clean() ); // Les entêtes sont déjà envoyées if( headers_sent($sentFile, $sentLine) ) { throw new \Exception('Can not stream, header already sent by '.$sentFile.':'.$sentLine); } // Entête du type event-stream header('Content-Type: text/event-stream'); // Désactive le cache client header('Cache-Control: no-cache'); |
Voici un exemple de flux en PHP.
1 2 3 4 5 6 7 8 |
$data = array('Ça va', 'Ça ne va plus'); $i = 0; do { echo "data: ".$data[$i%2]."\n\n"; flush(); $i++; sleep(1); } while( !connection_aborted() ); |
Je l’ai utilisé pour renvoyer une console SSH vers mon navigateur, pour ça, je vous conseille de voir cet article sur le Live stream d’une console SSH en PHP.
L’Event Source en JavaScript
Côté JavaScript, l’event source fonctionne entièrement en programmation orientée événement, ce qui signifie que vous n’allez pas interagir avec, juste réagir aux événements.
1 2 3 4 5 6 7 8 9 10 11 12 |
var source = new EventSource('http://mondomain.fr/page-event-stream.php'); source.addEventListener('message', function(e) { // Affiche le message dans la console (F12) console.log("Nous avons reçu le message : "+e.data); }, false); source.addEventListener('error', function(e) { console.log("Erreur :", e); if( e.readyState == EventSource.CLOSED ) { // La connexion est fermée } }, false); |
Il y a des événements qui peuvent être retournés par l’API, comme « error » et « open » mais autrement, vous pouvez enregistrer un événement selon le type que vous pouvez recevoir, si aucun listener n’est trouvé pour un type de message, le message est ignoré.
Ainsi, si mon serveur envoie des événements de type « status », je pourrais faire :
1 2 3 |
source.addEventListener('status', function(e) { console.log("Nouveau statut :", e.data); }, false); |
Il est ainsi très simple de recevoir des événements depuis notre application PHP mais il faut garder à l’idée que c’est unidirectionnel et que vous ne pouvez ni interagir avec le flux, ni le modifier, c’est le serveur qui décide ce qu’il envoie.
Après vous pouvez faire des scripts plus complexes qui vont permettre au script PHP de récupérer des commandes via un autre appel en AJAX.
Sources
Utilisation des envois d’événements côté serveur (server-sent events) sur mozilla.org
Stream Updates with Server-Sent Events sur html5rocks.com [en]