Protocol Buffers : une alternative optimisée à XML

Vous souhaitez stocker des données de manière rapide et légère sur un serveur web ?
Vous trouvez MySQL trop lent et pas assez portable ?
Vous avez pensé à XML mais vous le trouvez trop verbeux ?
Vous cherchez une solution simple qui ne requiert pas de configuration particulière du serveur ?

Alors pour vous, il y a Protocol Buffers !


Qu’est-ce que Protocol Buffers ?

Protocol Buffers est un système de sérialization de données créé par Google. Aujourd’hui, Protocol Buffers est en open source et est donc utilisable par tout le monde. Pourquoi avoir inventé Protocol Buffers alors qu’il existe déjà XML ?  En comparaison avec XML, Protocol Buffers est, selon Google, plus simple, moins ambigü, 3 à 10 fois plus léger, 20 à 100 fois plus rapide et génère des classes d’accès plus faciles à manipuler en programmation. Prenons tout de suite un exemple : imaginons que nous souhaitions modéliser une person avec un name et un email, voici le code en XML :

Code : XML

<personne>
    <nom>John Doe</nom>
    <mail>jdoe@example.com</mail>
</personne>

Encodé au format binaire, ce code a une taille de 69 bytes et est parsé entre 5 000 à 10 000 nanosecondes. Voyons dès maintenant ce qu’il en est pour protocol buffers :

Code : AUTRE

personne {
nom: "John Doe"
mail: "jdoe@example.com"
}

Encodé au format binaire, ce code a une taille de 28 bytes et est parsé entre 100 et 200 nanosecondes ! Une taille 3 fois moins importante et une vitesse 50 fois plus rapide ce qui n’est pas négligeable pour la quantité de données qui s’échange dans les serveurs de Google par exemple.

Comment ça marche ?

En PHP, on commence par créer un fichier .proto qui définit un objet, aussi appelé message. Puis on le compile avec le parseur, on obtient alors une classe PHP5 et enfin, avec cette classe, vous pouvez créer des instances du message, en charger depuis un fichier ou encore en enregistrer. C’est d’ailleurs à peu près le même fonctionnement pour tous les langages dans lequel Protocol Buffers est implémenté (17 si je ne me trompe), encore faut-il générer la classe pour le bon langage mais les fichier .pb (protocol buffers) qui contiennent les données sont utilisables par tous les langages.

Plus d’informations sur le site officiel.

Je pense que vous savez à peu près tout ce qu’il faut savoir pour commencer, c’est parti pour la pratique.

Installation du script

Nous allons donc commencer par télécharger Protocol Buffers (que je vais d’ailleurs abréger PB par la suite) pour PHP. Rendez vous sur le site de PB pour PHP : http://code.google.com/p/pb4php/

Téléchargez le fichier protocolbuf.zip, extractez son contenu. Vous devriez avoir un dossier protocolbuf avec dedans trois dossiers : parser, message et example. Vous n’avez rien besoin de plus :) .

Passons maintenant à la création d’un message.

Créez vos messages

Commencez par créer un fichier annuaire.proto. Les fichiers .proto sont les fichiers contenant les messages.

Dans cet exemple, nous allons créer un message Annuaire qui contiendra un tableau de messages Personne qui contiendra des informations sur des personnes et dans lequel il y aura un message Telephone qui contiendra le type de téléphone et son numéro.

Voici comment déclarer un message avec PB :

Code : AUTRE

message Nom_du_message {
	// Un commentaire
	// Ici le contenu du message
}

Vous pouvez en créer plusieurs dans un fichier, soit à la suite soit imbriqués.

Code : AUTRE

message Un_message {
	message Un_message_imbrique {

	}
}
message Un_message_a_la_suite {

}

Créons donc un message Annuaire, un autre Personne dans lequel il y en aura un autre appelé Telephone.
Voici à quoi votre fichier devrait ressembler.

Code : AUTRE

message Personne {

	message Telephone {

	}
}

message Annuaire {

}

Dans ces messages, nous allons créer des champs sous cette forme :

Code : AUTRE

type_champs type nom_champs = id;

type_champs peut être trois choses :
- optional : le champs n’est pas obligatoire
- required : le champs est obligatoire
- repeated : ce champs contient plusieurs valeurs, comme un tableau
Pour le type optional, on peut définir une valeur par défaut de cette manière :

Code : AUTRE

optional type nom_champs = id [default=valeur_par_defaut];

Si cette valeur par défaut est une chaîne de caractère, il faut la mettre entre guillemets.

type est le type de variable. Voici les principaux que vous serez amené à utiliser :
- float : nombre à virgule
- int32 : un chiffre
- string : une chaîne de caractères
Vous pouvez aussi utiliser le nom d’un autre message.
Exemples :

Code : AUTRE

repeated Personne personnes = 1;
optional string pays = 2 [default="France"];

nom_champs est tout simplement le nom du champs

id est l’identifiant unique du champs. Cet id doit être un chiffre et n’être présent qu’une seul fois dans le même message.

Mettons cela en pratique.

Dans le message Annuaire, créez un champs du nom de personnes qui contiendra plusieurs messages de type Personne.

Code : AUTRE

message Annuaire {
	repeated Personne personnes = 1;
}

Attention : vous devez toujours déclarer vos messages avant de vous en servir dans un champs. C’est pour cela que le message Personne est placé avant Annuaire.

Maintenant, nous allons nous occuper du message Telephone qui est, rappelez-vous, imbriqué dans le message Personne.
Dedans, nous allons stocker le type de numéro de téléphone. Pour cela, je vais vous enseigner un nouvel élément : les énumérations.
Voici leur syntaxe :

Code : AUTRE

enum Nom_enum {
	CHAMPS = id;
	CHAMPS2 = id;
}

L’id doit être un chiffre et ne pas apparaître 2 fois dans la même énumération.
Pour définir un champ qui utilise une énumération, voici comment procéder :

Code : AUTRE

type_champs Nom_enum nom_champs = id;

Rien ne change donc par rapport à un champ normal. Sauf si vous voulez utiliser une valeur par défaut, par exemple :

Code : AUTRE

optional Nom_enum nom_champs = 1 [default=CHAMPS2];

Créez donc dans le message Personne une énumération appelée TypeTelephone avec les types MAISON, TRAVAIL et PORTABLE.
Correction :

Code : AUTRE

message Personne {

	enum TypeTelephone {
		MAISON = 0;
		TRAVAIL = 1;
		PORTABLE = 2;
	}

	message Telephone {

	}
}

Occupons nous maintenant du message Telephone. Vous allez créer un champ obligatoire « numero » qui contiendra une chaîne avec le numéro de téléphone et un champ optionnel « type » qui contiendra le type de numéro de téléphone et dont la valeur par défaut sera MAISON.

Code : AUTRE

message Personne {

	enum TypeTelephone {
		MAISON = 0;
		TRAVAIL = 1;
		PORTABLE = 2;
	}

	message Telephone {
		required string numero = 1;
		optional TypeTelephone type = 2 [default=MAISON];
	}
}

Et enfin nous allons remplir le message Personne.
Nous allons y créer deux champs obligatoires qui contiendront des chaînes de caractères et qui stockeront, un le nom et l’autre le prénom de la personne. Nous allons aussi créer un champ qui contiendra plusieurs numéros de téléphone.

Code : AUTRE

message Personne {

	required string prenom = 1;
	required string nom = 2;

	enum TypeTelephone {
		MAISON = 0;
		TRAVAIL = 1;
		PORTABLE = 2;
	}

	message Telephone {
		required string numero = 1;
		optional TypeTelephone type = 2 [default=MAISON];
	}

	repeated Telephone telephones = 3;
}

message Annuaire {
	repeated Personne personnes = 1;
}

Voilà, nos messages sont terminés, nous allons pouvoir passer à la compilation du fichier annuaire.proto.

Compiler un fichier proto

Commencez par créer un fichier protoc.php.
Commençons par y inclure le fichier protocolbuf/parser/pb_parser.php qui contient le parseur pour parser le fichier proto.

Code : PHP

<?php
include('protocolbuf/parser/pb_parser.php');
?>

Nous allons créer une instance de la classe PBParser de cette manière :

Code : PHP

<?php
include('protocolbuf/parser/pb_parser.php');
$parser = new PBParser();
?>

Et enfin, parsons le fichier annuaire.proto :

Code : PHP

<?php
include('protocolbuf/parser/pb_parser.php');
$parser = new PBParser();
$parser->parse('annuaire.proto');
?>

Si vous avez une erreur du type :

Code : AUTRE

Fatal error: Uncaught exception 'Exception' with message 'Protofile type Personne unknown!' ...

Cela peut venir d’un problème d’encodage. Pour ne pas avoir ce problème, encodez vos fichiers .proto en AINSI ou alors, c’est parce que vous vous êtes servi d’un message avant de l’avoir déclaré.

Si tout s’est bien passé, vous aurez un fichier pb_proto_annuaire.php de créé, c’est la classe d’accès aux messages. Rendez-vous dans la prochaine partie pour apprendre à vous en servir.

Manipuler les messages

Et bien nous y voilà, nous allons commencer par créer une instance d’un message. Voici comment procéder :
Il faut commencer par inclure la librairie pb_message.php puis inclure le fichier généré par le parseur à partir du fichier proto.

Code : PHP

<?php
include('protocolbuf/message/pb_message.php');
include('pb_proto_annuaire.php');
?>

Ensuite, nous allons créer une instance de Annuaire.
Voici comment créer l’instance d’un message :

Code : PHP

<?php
$instance = new Message();
?>

On aura donc :

Code : PHP

<?php
$annuaire = new Annuaire();
?>

Nous allons maintenant ajouter une personne dedans. Voici comment faire pour un champ repeated :

Code : PHP

<?php
$nouvel_objet = $message->add_nom_champs();
?>

Nous ferons donc :

Code : PHP

<?php
$personne = $annuaire->add_personnes();
?>

Comme le champ personnes est de type Personne, nous allons ajouter des informations à l’instance du message Personne créée dans $personne.
Voici comment modifier un champ :

Code : PHP

<?php
$instance->set_champs(valeur);
?>

Mettez donc le nom / prénom que vous voulez à cette personne.
Exemple :

Code : PHP

<?php
$personne->set_prenom('Jean');
$personne->set_nom('Dupont');
?>

Maintenant nous allons rajouter un numéro de téléphone à cette personne dans le champ telephones de la même manière que nous avons ajouté une personne à l’annuaire :

Code : PHP

<?php
$telephone = $personne->add_telephones();
?>

Maintenant, nous allons définir le numéro de téléphone de maison de Jean Dupont. Pour le type de téléphone, nous n’allons pas le définir car ce champ est optionnel et sa valeur par défaut est déjà MAISON. :

Code : PHP

<?php
$telephone->set_numero('01 23 45 67 89');
?>

Voilà, maintenant vous allez rajouter une nouvelle personne à l’annuaire du nom que vous voudrez mais cette personne devra avoir 2 numéros de téléphone : un de maison et un portable.
Je vais maintenant vous montrer comment on va définir le type de téléphone.
L’énumération TypeTelephone est imbriquée dans le message Personne donc, son nom est Personne_TypeTelephone.
Maintenant voici comment définir le type de téléphone :

Code : PHP

<?php
$telephone->set_type(Personne_TypeTelephone::TRAVAIL);
?>

Correction :

Code : PHP

<?php
// On la créé et on lui définit son nom et son prénom
$personne = $annuaire->add_personnes();
$personne->set_prenom('John');
$personne->set_nom('Doe');

// On lui ajoute un numéro de maison
$telephone = $personne->add_telephones();
$telephone->set_numero('02 57 16 48 64');

// Et le numéro de portable
$telephone = $personne->add_telephones();
$telephone->set_numero('06 57 16 48 64');
$telephone->set_type(Personne_TypeTelephone::PORTABLE);
?>

Récupérer les informations et les afficher

Maintenant que notre annuaire est bien garni, nous allons afficher son contenu.
Voici comment nous allons procéder : nous allons commencer par récupérer le nombre de personnes dans l’annuaire, puis, dans une boucle, nous allons à chaque fois récupérer une personne, afficher son nom, prénom, puis nous allons récupérer le nombre de numéros de téléphone de cette personne et nous allons afficher ses numéros.
C’est parti !

Tout d’abord, récupérons le nombre de personnes dans l’annuaire :

Code : PHP

<?php
// Récupérer le nombre d'éléments dans un champs repeated :
// $instance->nomChamps_size();
$nb_personnes = $annuaire->size_personnes();
$i = 0;
while($i < $nb_personnes) {
	// Suite du code ici
	$i++;
}
?>

Ensuite, nous allons récupérer la personne correspondant à l’offset $i et afficher son nom et son prénom :

Code : PHP

<?php
	// Récupérer un élément dans un champs required :
	// $instance->nomChamps(offset);
	$personne = $annuaire->personnes($i);

	// Récupérer la valeur d'un champs : $instance->nomChamps();
	echo '<h3>'.$personne->prenom().' '.$personne->nom().'</h3>';
	//Suite du code ici
?>

Maintenant, vous avez toutes les clés en main pour faire la suite du code :
- Récupérez le nombre de numéros de téléphone
- Faites une boucle pour les explorer
- Récupérez un numéro de téléphone
- Affichez son type et le numéro

Correction :

Code : PHP

<?php
$nb_personnes = $annuaire->personnes_size();
$i = 0;
while($i < $nb_personnes) {
	$personne = $annuaire->personnes($i);
	echo '<h3>'.$personne->prenom().' '.$personne->nom().'</h3>';

	// Début correction
	$nb_telephones = $personne->telephones_size();
	$j = 0;
	while($j < $nb_telephones) {
		$telephone = $personne->telephones($j);
		echo '<strong>'.$telephone->type().'</strong> : ';
		echo $telephone->numero().'<br />';
		$j++;
	}
	// Fin correction
	$i++;
}
?>

Comme vous le voyez, pour le type de téléphone, ça nous affiche l’id. Comment remédier à cela ?
En créant simplement un tableau qui, à chaque id, associe ce que l’on doit afficher.
Ici par exemple :

Code : PHP

<?php
$types = array(0 => 'Maison', 1 => 'Travail', 2 => 'Portable');
?>

Et pour l’afficher :

Code : PHP

<?php
echo '<strong>'. $types[$telephone->type()] .'</strong> : ';
?>

Enregistrer un message

Voici comment récupérer la chaîne de caractère qui contient les données codées :

Code : PHP

<?php
$instance->serializeToString();
?>

Voci donc comment enregistrer notre annuaire :

Code : PHP

<?php
$chaine = $annuaire->serializeToString();
$fp = fopen('fichier.pb', 'wb');
fwrite($fp, $chaine);
fclose($fp);
?>

Récupérer un message

Voici comment récupérer un message :

Code : PHP

<?php
$instance = new Votre_message();
$instance->parseFromString($chaine);
?>

Voici donc comment récupérer notre annuaire :

Code : PHP

<?php
$annuaire = new Annuaire();

$chaine = '';
$fp = fopen('fichier.pb', 'r');
while(!feof($fp)) {
	$chaine .= fgets($fp);
}
fclose($fp);

$annuaire->parseFromString($chaine);
?>

Voilà, cette introduction à Protocol Buffers est terminée, j’espère que vous trouverez plein de moyens d’application à ce que vous venez d’apprendre !

Partagez l'article :
  • Twitter
  • Facebook
  • Google Bookmarks
  • LinkedIn
  • email
  • Print
  • del.icio.us
  • Live
  • MySpace
  • Netvibes
  • Digg
  • Wikio
  • RSS
Mots-clefs : 

Laissez un commentaire