Script PHP utilisé localement, avec interface HTML 5 grâce à WebSocket

Pour donner à un script natif une interface HTML 5, nous utilisons Node.js et WebSocket afin d'utiliser au mieux les données du script dans l'interface.

Dans une précédente étude nous avons vu comme utiliser localement une interface HTML avec un script PHP, cela grâce à un serveur Node.js. La méthode cependant est trop simple puisque les résultats du script sont affichés tels quels dans le navigateur. Cela peut convenir à certaines applications, mais si l'on veut une interface plus interactive, il faut que l'on puisse gérer les données envoyées par le script quand elles sont reçues par la page HTML.
Pour ce faire nous utilisons deux modules:

Net

Ce module est intégré par défaut dans Node.js et il permet de se connecter par sockets à un programme dans le système de fichier en créant un serveur TCP. On peut bien sûr se connecter à un script distant, mais c'est hors de notre propos.
Le serveur lance le script PHP avec les arguments fournis. Ce peut être en fait un script dans n'importe quel langage de programmation pourvu qu'il supporte les connections TCP. Le script effectue le traitement auquel il est dédié et envoie au serveur le résultat avec la fonction PHP socket_write, ou équivalent dans un autre langage.
Pour utiliser TCP avec PHP sous Windows, il faut activer deux lignes dans php.ini (supprimer le point-virgule):

extension_dir="ext"
extension=php_sockets.dll 

Socket.io

Ce module doit être ajouté avec npm, s'il ne l'est pas déjà. On l'utilise pour échanger des informations entre l'interface HTML et Node.js. L'interface envoie les arguments donnés dans l'interface et le nom du script au serveur, cela par WebSocket.
Quand le serveur reçoit ensuite les résultats du script, il les envoie à l'interface toujours par WebSocket.

Le diagramme de fonctionnement est donc le suivant:

PHP et HTML par WebSocket et Node.js

Le script PHP

Les données provenant de l'interface étant passées au script comme argument, il n'y a rien à changer s'il est conçu pour être utilisé en ligne de commande (c'est le propre d'un script).

Pour l'envoi des résultats, qui étaient auparavant simplement affichés dans la console on remplace:

echo $data;

par une fonction:

function echoSocket($data)
{
  $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  if($socket==false) die("Not created");
  socket_connect($socket, '127.0.0.1', '1001')
    or die(socket_strerror(socket_last_error()));
  socket_write($socket, $data);
  socket_close($socket);
}

echoSocket("You choosen: <b><span style='color:$data'>$data</span></b>.");

La fonction crée une connection TCP locale sur le port 1001, envoie les données et referme la connection.

L'interface HTML

On crée une instance de WebSocket avec le module socket.io dans la page HTML:

<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect();
... 

La page de démonstration demande à l'utilisateur de choisir une couleur.

<select id="color">...</select>
<input type="button" value="Send color" onClick="callserver()">

Quand il clique sur le bouton, l'information est envoyée à Node par cette fonction:

function callserver()
{
  var mycolor = document.getElementById("color").value;
  socket.emit('interface', mycolor);
}

Elle attend la réponse du serveur:

socket.on('notification', function(x) { notification(x); });	

Elle traite le contenu envoyé par le script PHP. Elle ajoute <br> et la réponse dans une zone de l'interface:

function notification(content)
{
  var x=document.getElementById("storage");
  x.innerHTML += content + '<br>';
}

L'interface HTML donc utilise les données fournie par le programme natif selon ses besoins.

Limitations

Si vous voulez que votre interface HTML puisse charger un fichier CSS ou des images, il faut ajouter la possibilité de charger ces fichiers au script JavaScript du serveur. Cela sera vu dans un article ultérieur. Pour l'instant le code CSS doit être intégré à la page.

Le serveur JavaScript

On crée une instance de WebSocket pour communiquer avec la page HTML:

var websocket = require("socket.io");
var app = http.createServer(function(r, s){ handler(r,s); });
app.listen(1000);
var listener = websocket.listen(app);

Et une instance de Net pour recevoir les données du script natif:

var net = net = require('net');
var nativeserver = net.createServer(function(native) { nativeComm(native);});
nativeserver.listen(1001, '127.0.0.1');

Quand le serveur reçoit des données sur le port 1000 en provenance de l'interface HTML, il lancer le script local avec ces données en paramètres.

websocket.on('interface', function (data) {
  fs.exists(localpath, function(result) { runScript(result, localpath, data)});
});

Quand le serveur reçoit des données du script natif sur le port 1001, il les retransmet sur le port 1000 à l'interface HTML:

native.on('data', function(data) {
  listener.sockets.emit('notification', data);
});

Pour le source complet, télécharger la démonstration...

Télécharger les fichiers PHP, JavaScript et HTML

Les fichiers de l'archive utilisés par cette démonstration sont:

Pour essayer la démo, lancer le serveur:

node php-socket.js

Puis ouvrir le navigateur et taper dans la barre d'URL:

localhost:1000/php-socket.php

Le serveur affiche alors l'interface php-socket.html dans le navigateur. Choisir une couleur et cliquer sur le bouton pour envoyer les données au script PHP.

Ces éléments devraient être un point de départ suffisant pour créer une application locale complexe avec une interface HTML 5 et CSS en PHP, Python, Ruby ou tout autre langage fonctionnant en ligne de commande. Une telle application sera universellement portable et devrait même fonctionner nativement sur mobiles.

Pour une application pratique de ce procédé, voir Link Checker avec interface utilisateur graphique.