Exécuter localement un programme PHP à partir du navigateur avec Node.js

Node.js est le chaînon qui permet de relier le navigateur aux fichiers exécutables sur le poste local.

Le diagramme ci-dessous montre le fonctionnement du système:

  1. On lance Node avec le script serveur:
    node runphp.js
  2. On tape dans le navigateur l'URL locale du script:
    localhost:1000/dirlist.php
  3. Node lance l'exécution du script.
  4. Le script affiche un résultat avec la commande echo.
  5. Le résultat est envoyé par Node à une nouvelle page HTML.

PHP HTML et Node.js

Pour lancer le programme, on tape le nom du programme dans la barre d'URL:

localhost:1000/dirlist.php

Et l'on peut aussi passer des variables au programme à exécuter sous cette forme:

localhost:1000/dirlist.php?x=test 

Le code JavaScript du serveur est dérivé du code que l'on a déjà utilisé pour créer un serveur de fichiers HTML avec Node.js. Mais cette fois, au lieu d'afficher le contenu, on utilise le module child_process pour exécuter un programme, ce sont alors les résultats du programme qui seront affichés.
Cela fonctionne sur un poste local comme sur un hébergement distant.

1) Comme toujours on commence par créer un serveur

var server = http.createServer(php);
server.listen(1000);
console.log("PHP ready to run script given on port 1000.");

Et on assigne un port de communication.

2) On vérifie la présence du script dans le système de fichier

function php(request, response)
{
  var urlpath = url.parse(request.url).pathname;
  var param = url.parse(request.url).query;
  var localpath = path.join(process.cwd(), urlpath);
  fs.exists(localpath, function(result) { runScript(result, localpath, param, response)});
}

On extraie le nom du fichier de l'URL avec la méthode parse du module url.
Et en outre on extrait la chaîne de paramètres avec la méthode query. Elle est transmise telle quelle au script PHP.

3) On lance l'exécution du script

function runScript(exists, file, param, response)
{
  if(!exists) return sendError(404, 'File not found', response);
  runner.exec("php " + file + " " + param,
   function(err, stdout, stderr) { sendData(err, stdout, stderr, response); });
}

La méthode exec de child_process à pour premier paramètre ce que l'on tape dans la ligne de commande, donc le nom de l'interpréteur PHP, le nom du script et la liste des paramètres.

4) Tout ce qu'affiche le script sera affiché dans le navigateur

function sendData(err, stdout, stderr, response)
{
  if (err) return sendError(500, stderr, response);
  response.writeHead(200,{"Content-Type": "text/plain;charset=utf-8"});
  response.write(stdout);
  response.end();
}

C'est le rôle de la fonction sendData appelée en callback par la méthode exec.

5) Exemple de script PHP

Ce script PHP de démonstration lit les fichiers présents dans le répertoire et en affiche la liste. Tout ce qu'il affiche apparaîtra dans le navigateur.

<?php
echo "Parameter:".$argv[1]."\n";
echo "Directory content...\n\n";

$output="";
if ($hnd = opendir('.'))
{
  while($file = readdir($hnd))
  {
    if ($file == "." || $file == "..") continue;
    $output .= "$file\n";
  }
  closedir($hnd);
}
echo $output;
?>

Les paramètres sont retrouvés dans la tableau $argv. Le premier paramètre (le seul dans l'exemple) dans $argv[1]. Il appartient au programmeur de découper la chaîne et utiliser son contenu.

6) Le code source JavaScript complet

var http = require("http"),
path = require("path"),
fs = require("fs"),
url = require("url"),
runner = require("child_process");

function sendError(errCode, errString, response)
{
  response.writeHead(errCode, {"Content-Type": "text/plain;charset=utf-8"});
  response.write(errString + "\n");
  response.end();
  return false;
}

function sendData(err, stdout, stderr, response)
{
  if (err) return sendError(500, stderr, response);
  response.writeHead(200,{"Content-Type": "text/plain;charset=utf-8"});
  response.write(stdout);
  response.end();
}

function runScript(exists, file, param, response)
{
  if(!exists) return sendError(404, 'File not found', response);
  runner.exec("php " + file + " " + param,
   function(err, stdout, stderr) { sendData(err, stdout, stderr, response); });
}

function php(request, response)
{
  var urlpath = url.parse(request.url).pathname;
  var param = url.parse(request.url).query;
  var localpath = path.join(process.cwd(), urlpath);
  fs.exists(localpath, function(result) { runScript(result, localpath, param, response)});
}

var server = http.createServer(php);
server.listen(1000);
console.log("PHP ready to run script given on port 1000.");

Serveur local

En fait on dispose avec ce programme de la base d'un serveur d'applications local: en changeant le nom du script dans la barre d'URL, on peut lancer des outils différents.
Pour automatiser le lancement on peut mettre chacun en bookmark, et encore éventuellement utiliser des bookmarklets pour passer des paramètres.
Ce serveur local peut aussi être lancé lui-même en plaçant la commande ci-dessus dans un fichier batch qui sera exécuté au démarrage de la session.