Web services en PHP avec XML-RPC
Qu'est-ce qu'un "web service"?
Un "service web" est une application accessible au travers des protocoles
réseau et Internet actuels. Il ne s'agit plus d'une application
destinée directement à l'utilisateur humain (comme une page
web classique) mais d'un application destinée à éxécuter
certaines taches et à communiquer avec d'autres applications disponibles
ailleurs sur le réseau (local ou Internet). L'originalité
vient du fait que l'interface de ce service (la façon dont il communique
avec les autres applications) est définie de manière standardisée
et ouverte. En fait, l'interface d'un service web repose uniquement sur
des messages qu'elle peut envoyer et recevoir. Cela signifie qu'un service
web peut être développé dans n'importe quel langage,
sur n'importe quelle plateforme, à condition qu'il soit en mesure
de traiter et d'envoyer des messages codés selon un format précis,
convenu d'avance et supporté de manière uniforme par les
différents acteurs.
En quoi les web services peuvent-ils être utiles?
Supposons que vous ayez besoin de faire bénéficier vos visiteurs
d'un répertoire téléphonique. Plusieurs solutions
s'offrent à vous: (1) vous développez une application de
répertoire téléphonique. Cela prend du temps, des
moyens, cela ne corresponds pas à votre métier. (2) vous
utilisez une offre logicielle commerciale. Cela va couter cher, prendra
nécessairement du temps à installer et à intégrer
dans votre système. Dans les deux cas, cela nécessite la
présence d'un système de gestion de base de données.
(3) vous faites appel à un service web de répertoire téléphonique.
Cela vous permet de stocker et de récupérer vos numéros
de téléphone sur un serveur distant. La standardisation de
l'interface fait en sorte que de très nombreux logiciels peuvent
accéder à ces services.
Quels sont les problèmes posés par les services web?
Ils sont de plusieurs ordres :
- identification des acteurs : supposons que le stockage d'un nouveau
numéro vous coute 10 centimes. Il faut donc empecher que n'importe
qui envoie de nouveau numéros au service web en se faisant passer
pour vous.
- sécurisation des transmissions : les données concernant
vos contacts téléphoniques vont circuler sur le réseau
(Internet et local). Si celles-ci sont sensibles, il est nécessaire
de sécuriser vos transmissions (par exemple au moyen d'un tunnel
SSL)
- confiance en ces acteurs : les fournisseurs du service web ont accès
à tous vos contacts téléphoniques, puisque ceux-ci
sont stockés sur leur serveur; est-ce que ces fournisseurs sont
dignes de confiance?
- disponibilités des serveurs sur lesquels sont stockés
les services web : en cas d'incident technique, de crash de la machine
serveur, le services web concerné risque d'être indisponible.
Que faire pour assurer une continuité de service?
- gestion de la notion de transaction : une procédure éxécutée
par un service web peut comprendre une série de sous-procédures.
Si l'une d'entre elles ne réussit pas, il ne faut bien entendu pas
forcément que le service soit facturé...
Techniquement, sur quoi reposent les "web services"?
Les services web sont basés sur des appels de procédures
distantes (Remote Procedure Calls). Un client envoie un message à
un programme serveur, en lui demandant d'éxécuter une procédure
bien précise, avec des paramètres spécifiés
dans le message. La nouveauté vient du fait que les messages sont
codés au format XML, ce qui il existe de nombreux outils permettant
de traiter le XML (parseurs). Il est même possible de transformer
un format XML en d'autres formats, XML ou non.
Le transport des messages se fait au moyen de HTTP pour les messages
synchrones et de SMTP pour les messages asynchrones.
Création et deploiement d'un "web service" simple grâce à
XML-RPC
Nous vous proposons ici de batir votre premier "web service". Il s'agit
d'un répertoire téléphonique simple, doté de
capacités d'authentification basiques. Pour stocker les informations
(numéros de téléphone), nous utiliserons une base
de données MySQL.
Par ailleurs, pour mettre en place ce web service et y acceder, nous
utiliserons une bibliothèque de fonctions PHP implémentant
le protocole XML-RPC. XML-RPC est un format mis au point par Userland Software
permettant d'effectuer des appels de procédure distantes via XML.
Cette bibliothèque est disponible à l'adresse http://www.xmlrpc.com
Dans un premier temps, nous allons créer la table et y ajouter
un numéro (le mien) :
create table repertoire (nom varchar(30), telephone varchar(15));
insert into repertoire values ("Olivier Elemento", "0615025055");
Puis nous allons créer la partie serveur du web service.
Ce web service contient deux fonctions : addPhoneNumber() qui permet de
stocker un numéro et getPhoneNumber($params) qui permet de
récupérer un numéro à partir d'un nom spécifié.
<?
include("xmlrpc.inc");
include("xmlrpcs.inc");
include("db.inc.php");
function getPhoneNumber($params) {
$nameObj = $params->getParam(0);
$name = $nameObj->scalarval();
$query = "SELECT nom, telephone FROM repertoire WHERE nom LIKE '%$name%'";
$result_id = mysql_query($query);
while (list($nom, $telephone) = mysql_fetch_array($result_id)) {
$tableau[$nom] = "$telephone";
}
return new xmlrpcresp(xmlrpc_encode($tableau));
}
function addPhoneNumber($params) {
$nameObj = $params->getParam(0);
$teleObj = $params->getParam(1);
$nom = $nameObj->scalarval();
$tel = $teleObj->scalarval();
$query = "INSERT INTO repertoire VALUES ('$nom', '$tel')";
$result_id = mysql_query($query);
if ($result_id)
return new xmlrpcresp(new xmlrpcval(1, 'int'));
else
return new xmlrpcresp(new xmlrpcval(0, 'int'));
}
$s = new xmlrpc_server(array("getPhoneNumber" => array("function" => "getPhoneNumber"), "addPhoneNumber" => array("function" => "addPhoneNumber")));
?>
Authentification
Maintenant que nous avons en web service fonctionnel, il va être
nécessaire d'en restreindre l'accés. Pour ce faire, nous
utiliserons une authentification HTTP classique et simple à mettre
en oeuvre. L'authentification HTTP permet de restreindre l'accès
d'un répertoire (et de ses sous-répertoires) aux visiteurs
fournissant nom d'utilisateur et mot de passe valides.
1ère étape : on crée d'abord un fichier nommé
.htaccess, ayant la forme suivante :
AuthUserFile /chemin/vers/.htpasswd
AuthGroupFile /dev/null
AuthName "ma zone secrète"
AuthType Basic
<Limit GET POST>
require valid-user
</Limit>
2eme étape : on crée un fichier nommé .htpasswd avec
la commande .htpasswd, en lui spécifiant un nom d'utilisateur. htpasswd
vous demande alors d'entrer un mot de passe.
htpasswd -c .htpasswd olly
Taper toto. Cela créera le fichier .htpasswd puis y stockera le
mot de passe de "olly" sous forme cryptée. Si le fichier existe
déjà et que l'on souhaite lui ajouter un utilsateur, on tapera:
htpasswd .htpasswd pierrot
Pour supprimer un utilisateur, il suffit d'ouvrir le fichier avec un éditeur
et d'y supprimer la ligne faisant réference à l'utilisateur.
Accés à ce web service.
Maintenant que nous avons créé un service web, il
va falloir y accéder. Pour cela il faut créer un "client",
cad à dire un programme qui va invoquer les fonctions du service
web distant et en récuperer les résultats.
<html>
<?
include("xmlrpc.inc");
/** crée un nouveau client **/
$c=new xmlrpc_client("/~olly/xmlrpc/repertoire_serveur.php", "localhost", 80);
/** user et mot de passe permettant d'accéder au service web distant **/
$c->setCredentials("olly", "toto");
/** crée un nouveau message : ajoute un numéro de téléphone **/
$ajout=new xmlrpcmsg('addPhoneNumber', array(new xmlrpcval("Olivier Jones", "string"), new xmlrpcval("65765544333", "string")));
/** envoie le message et stocke le retour dans $r **/
$r=$c->send($ajout);
$v=$r->value();
if (!$r->faultCode()) {
$arr = $v->scalarval();
print $arr;
} else {
print "Fault: ";
print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'<BR>";
print "<HR>I got this value back<BR><PRE>" .
htmlentities($r->serialize()). "</PRE><HR>\n";
}
$f=new xmlrpcmsg('getPhoneNumber', array(new xmlrpcval("Olivier", "string")));
//$c->setDebug(1);
$c->setCredentials("olly", "batman");
$r=$c->send($f);
//$c->setDebug(1);
$v=$r->value();
if (!$r->faultCode()) {
$arr = xmlrpc_decode($v);
//print $arr;
while (list($key, $val) = each($arr)) {
print "<li>$key => $val";
}
} else {
print "Fault: ";
print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'<BR>";
print "<HR>I got this value back<BR><PRE>" .
htmlentities($r->serialize()). "</PRE><HR>\n";
}
?>
Ressources
XML-RPC Library for Java