Server-Sent Events #1

Cette fois, je veux que mes mises à jour sur le site soient annoncées en temps réel. Plusieurs solutions s’offrent à moi : les Websockets, les Server-Sent Events et le long polling.
Chacune de ces solutions a ses avantages et inconvénients. Le livre Data Push Apps with HTML5 SSE de Darren Cook explique bien ces trois technologies.
Etant donné que je ne cherche pas une solution full duplex et que je ne veux rien changer sur mon serveur (qui n’est pas dédié), je peux me satisfaire des SSE ou du long polling. Les étapes de ce développement vont être les suivantes :

  • création d’un plugin WP qui utilise Ajax
  • modification du plugin pour utiliser les SSE
  • déclenchement du plugin sur réception d’un événement

création d’un plugin WP qui utilise Ajax

J’ai plusieurs fois développé des plugins WP mais jusqu’à présent c’était de façon non académique. Cette fois je serai plus rigoureux…. J’utilise ces recommandations et ce tutoriel : http://www.id-meneo.com/blog/introduction-a-lutilisation-dajax-dans-wordpress-traduction Je ne répète pas ce qui est dans ce tutoriel. Il y a deux fichiers : helloworld.php et ajax.js et un shortcode en action ici

helloworld.php

<?php
/*
Plugin Name: Hello World Ajax Frontend 2
Plugin URI: http://rainrain.com
Description: A simplified ajax front end
Version: 2
Author: Bob Murphy
Author URI: http://rainrain.com
*/


// enqueue and localise scripts
wp_enqueue_script( 'my-ajax-handle', plugin_dir_url( __FILE__ ) . 'ajax.js', array( 'jquery' ) );
wp_localize_script( 'my-ajax-handle', 'the_ajax_script', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );

// THE AJAX ADD ACTIONS
add_action( 'wp_ajax_the_ajax_hook', 'the_action_function' );
add_action( 'wp_ajax_nopriv_the_ajax_hook', 'the_action_function' ); // need this to serve non logged in users

// THE FUNCTION
function the_action_function(){

/* this area is very simple but being serverside it affords the possibility of retreiving data from the server and passing it back to the 
javascript function */

$name = $_POST['name'];
echo"Hello World, " . $name; // this is passed back to the javascript function
die(); // wordpress may print out a spurious zero without this – can be particularly bad if using json
}

// ADD EG A FORM TO THE PAGE
function hello_world_ajax_frontend(){
	echo '
         <form id="theForm">
            <input id="name" name="name" value = "name" type="text" />
            <input name="action" type="hidden" value="the_ajax_hook" />&nbsp; <!– this puts the action the_ajax_hook into the serialized form –>
            <input id="submit_button" value = "Click This" type="button" onClick="submit_me();" />
        </form>
 		
 		<div id="response_area">
        	This is where we\'ll get the response
        </div>
 	' ; 
 }

add_shortcode("hw_ajax_frontend", "hello_world_ajax_frontend");

?>

ajax.js

function submit_me(){

// get name from form input
var thename = jQuery("input#name").val();

jQuery.post(the_ajax_script.ajaxurl, 
	jQuery("#theForm").serialize(),
	function(response_from_the_action_function){
		jQuery("#response_area").html(response_from_the_action_function);
	}
);

}

Exemple de code SSE

Pour tester le SSE, je vais partir d’un logiciel qui permet d’afficher un message toutes les 5 secondes.

s1.html

Le code de s1.html utilise EventSource

L’interface EventSource est utilisée afin de recevoir des évènements envoyés par le serveur. Elle se connecte à un serveur via HTTP et reçoit des évènements au format text/event-stream avant de clôturer la connexion.

et addEventListener

The EventTarget.addEventListener() method adds the specified EventListener-compatible object to the list of event listeners for the specified event type on the EventTarget on which it’s called. The event target may be an Element in a document, the Document itself, a Window, or any other object that supports events (such as XMLHttpRequest).

 <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Basic SSE Example</title>
        <s src="//code.jquery.com/jquery-1.11.0.min.js"></s>
      </head>
      <body>
      	Hello 
        <pre id="x">Initializing...</pre>
        <s>
        var es = new EventSource("basic_sse.php");
        es.addEventListener("message", function(e){
          $("#x").fadeOut("fast", function(){
            $("#x").html(e.data);
            $("#x").fadeIn("slow");
            });
          },false);
        </s>
        End Hello	
      </body>
    </html>

basic_sse.php

<?php
    header("Content-Type: text/event-stream");
    while(true){
      echo "data:".date("Y-m-d H:i:s")."\n\n";
      @ob_flush();@flush();
      sleep(5);
    }

Analyse de la fonction submit_me()

Cette fonction est appelée lorsque l’utilisateur clique sur le bouton

input id="submit_button" value = "Click This" type="button" onClick="submit_me();"

jQuery.post a les paramètres suivants :

  • URL : A string containing the URL to which the request is sent
  • data :  plain object or string that is sent to the server with the request
  • success : A callback function that is executed if the request succeeds.

Dans notre cas :

  • URL : the_ajax_script.ajaxurl
  • data =: jQuery(« #theForm »).serialize()
  • callback : function(response_from_the_action_function){
    jQuery(« #response_area »).html(response_from_the_action_function); }

Dans le fichier PHP, on a cette ligne :

wp_localize_script( 'my-sse_ajax-handle', 'the_ajax_script', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );

Although wp_localize_script() is created for localization, it also has another great use. You can declare javascript variables with namespaces to use with your script. Here’s the syntax:

wp_localize_script( $handle, $namespace, $variable_array );

On a donc la variable the_ajax_script qui pointe vers notre script sse_test.js et the_ajax_script.ajaxurl vers l’URL.
Je n’insiste pas sur jQuery(« #theForm »).serialize()

Quant au callback, on voit qu’il récupère la réponse d’appel PHP et remplace #response_area

function submit_me(){
 
// get name from form input
var thename = jQuery("input#name").val();
 
jQuery.post(the_ajax_script.ajaxurl, 
    jQuery("#theForm").serialize(),
    function(response_from_the_action_function){
        jQuery("#response_area").html(response_from_the_action_function);
    }
);

}

Je vais mixer les 2 tutoriels dans le prochain exemple.