fév 21

Dans ce petit billet, nous allons tâcher de présenter simplement ce que sont les services web REST et le format d’échange JSON.

L’architecture REST

Commençons par le commencement. Une piqure de rappel sur le protocole HTTP n’est jamais malvenue.
Pour faire simple, une requête HTTP se présente ainsi :

  • Une méthode (GET, POST, PUT, DELETE)
  • Un chemin vers une ressource (/index.php, /liste/contenu/)
  • Une version du protocole (1.0 ou 1.1 généralement)
  • Un ensemble de propriétés et valeurs (Content-Type, Length)
  • Un contenu (facultatif)

C’est autour de ce protocole que s’organise l’architecture REST.

Les méthodes HTTP sont donc utilisées ainsi :

  • GET : lecture d’information
  • POST : envoi d’information
  • PUT : mise à jour d’information
  • DELETE : suppression d’information

Par exemple, si je veux lire la liste des adhérents à mon club d’échecs, j’effectue une requête de type GET vers la ressource http://monsite.com/adherents . Si je veux les adhérents ayant plus de 20 ans, je peux faire http://monsite.com/adherents?ageMinimum=20.
De la même manière, si je veux supprimer les adhérents 3 et 4, je peux faire une requête de type DELETE vers http://monsite.com/adherents/3/4.
Si on veut envoyer des informations, on utilise POST ou PUT en passant les informations dans le corps du message.

REST du côté serveur

La plupart d’entre vous utilisent PHP pour interpréter les informations du côté du serveur. Nous allons donc nous baser sur ce langage afin d’expliquer le fonctionnement.

Tout d’abord, il faut savoir quelle est la méthode utilisée. Et là, premier problème : PHP ne semble gérer que les requêtes de type POST et GET, qui sont utilisées par HTML.

Récupérons d’abord la méthode HTTP

Pour cela, il suffit d’utiliser les données du serveur.

// $method sera égal à 'get', 'post', 'put' ou 'delete'
$method = strtolower($_SERVER['REQUEST_METHOD']);

Pas de soucis pour GET et POST

Les méthodes GET et POST ne posent pas de soucis particulier : leur contenu est passé respectivement dans les tableaux $_GET et $_POST.

Pas de soucis non plus pour DELETE

La méthode DELETE envoie ses paramètres dans l’URL ( accessible via $_SERVER['REQUEST_URI'])

Il reste les paramètres de PUT

La méthode PUT envoie elle des informations dans le corps du message. Comment les récupérer en PHP ?
Voici la solution :

// récupération des données brutes
$raw_data = file_get_contents('php://input');
// transformation en tableau à indexes ($put_data)
parse_str($raw_data, $put_data);

Format des échanges : JSON

Les données échangées doivent pouvoir être comprises par le côté serveur comme le côté client.
Pour cela, un format simple avec une forte capacité de compression a fait son apparition : JSON.
JSON s’architecture autour du format des objets ECMAScript (JavaScript) .
Les 2 principales caractéristiques sont :

  • Le principe de clé / valeur
  • L’organisation des données sous forme de tableau

Les types de données valables sont :

  • tableau
  • objet
  • chaîne de caractères
  • valeur numérique (entier, double)
  • booléen (true/false)
  • null

Par exemple, pour récupérer le nom, le prénom et l’âge d’une personne dans un objet :

{
    "nom": "xxxx",
    "prenom": "yyyy",
    "age": 25
}

Une liste d’articles :

[
    {
        "nom": "article a",
        "prix": 3.05,
        "disponible": false,
        "descriptif": "mon article a ..."
    },
    {
        "nom": "article b",
        "prix": 13.05,
        "disponible": true,
        "descriptif": null
    }
]

Vous trouverez plus d’informations sur le format JSON au travers du site officiel : http://www.json.org/json-fr.html

Pour les données binaires (images et autres), il suffit de donner une adresse en lecture et une propriété en écriture.
Par exemple, si je veux envoyer une image avec un descriptif, mes variables POST seront organisées ainsi :

  • json : descriptif au format json
  • image : image au format binaire

La lecture des données au niveau serveur se fait ainsi :

$result = json_decode($json_data);

L’écriture, basée sur un tableau à indexes, se fait ainsi :

$json_str = json_encode($data_array);

Vous trouverez toutes les informations nécessaires sur http://www.php.net/manual/fr/ref.json.php.

Pour finir, le type de données utilisé généralement pour décrire un contenu JSON est application/json.

Avec ceci, vous avez de quoi réaliser des services web performants et simples à mettre en oeuvre !

  • Share/Bookmark
jan 30

Les développeurs PHP vont certainement adorer cette fonction.

Elle permet d’encoder un tableau (à indexes ou pas) de manière efficace. Elle est basée sur un autre code qui ne fonctionnait malheureusement pas très bien.
Les CDATA sont également automatiquement insérés au besoin.
Quand il n’y a pas d’index, les données ressortent sous l’intitulé entry et la racine du document sera toujours entries.

function xml_encode($mixed, $domElement = NULL, $DOMDocument = NULL) {
    if (is_null($DOMDocument)) {
        $DOMDocument = new DOMDocument;
        $DOMDocument->formatOutput = true;
 
		$rootNode = $DOMDocument->createElement('entries');
		$DOMDocument->appendChild($rootNode);
 
        xml_encode($mixed, $rootNode, $DOMDocument);
 
        echo @$DOMDocument->saveXML();
    } else {
        if (is_array($mixed)) {
            foreach ($mixed as $index=>$mixedElement) {
                if (is_int($index)) {
					$nodeName = 'entry';
				} else {
					$nodeName = $index;
				}
				$node = $DOMDocument->createElement($nodeName);
				$domElement->appendChild($node);
				xml_encode($mixedElement, $node, $DOMDocument);
			}
        } else {
			// TODO: test if CDATA if needed
			$new_node = $DOMDocument->createTextNode($mixed);
 
            $domElement->appendChild($new_node);
        }
    }
}

On l’utilise comme json_encode().

Par exemple :

<?php
 
header('Content-Type: text/xml');
 
function xml_encode($mixed, $domElement = NULL, $DOMDocument = NULL) {
    if (is_null($DOMDocument)) {
        $DOMDocument = new DOMDocument;
        $DOMDocument->formatOutput = true;
 
		$rootNode = $DOMDocument->createElement('entries');
		$DOMDocument->appendChild($rootNode);
 
        xml_encode($mixed, $rootNode, $DOMDocument);
 
        echo @$DOMDocument->saveXML();
    } else {
        if (is_array($mixed)) {
            foreach ($mixed as $index=>$mixedElement) {
                if (is_int($index)) {
					$nodeName = 'entry';
				} else {
					$nodeName = $index;
				}
				$node = $DOMDocument->createElement($nodeName);
				$domElement->appendChild($node);
				xml_encode($mixedElement, $node, $DOMDocument);
			}
        } else {
			// TODO: test if CDATA if needed
			$new_node = $DOMDocument->createTextNode($mixed);
 
            $domElement->appendChild($new_node);
        }
    }
}
 
$entree[0] = "texte";
 
for ($i = 0;$i < 50;$i++) {
	$entree[$i]['nom'] = "texte pour nom ${i}";
	$entree[$i]['valeur'] = "texte pour valeur ${i}";
 
	$donnes['debut'] = 0;
	$donnes['fin'] = 100;
	$entree[$i]['donnees'] = $donnes;
 
	for ($j = 0;$j < 5;$j++) {
		$liste[$j] = "liste ${j}";
	}
 
	$entree[$i]['liste'] = $liste;
 
	$entree[$i]['xml'] = "donnees avec <a>balise xml</a>";
}
 
echo xml_encode($entree);
 
?>

Affichera :

<?xml version="1.0"?>
<entries>
  <entry>dexte</entry>
  <entry>
    <nom>texte pour nom 1</nom>
    <valeur>texte pour valeur 1</valeur>
    <donnees>
      <debut>0</debut>
      <fin>100</fin>
    </donnees>
    <liste>
      <entry>liste 0</entry>
      <entry>liste 1</entry>
      <entry>liste 2</entry>
      <entry>liste 3</entry>
      <entry>liste 4</entry>
    </liste>
    <xml>donnees avec &lt;a&gt;balise xml&lt;/a&gt;</xml>
  </entry>
  <entry>
    <nom>texte pour nom 2</nom>
    <valeur>texte pour valeur 2</valeur>
    <donnees>
      <debut>0</debut>
      <fin>100</fin>
    </donnees>
    <liste>
      <entry>liste 0</entry>
      <entry>liste 1</entry>
      <entry>liste 2</entry>
      <entry>liste 3</entry>
      <entry>liste 4</entry>
    </liste>
    <xml>donnees avec &lt;a&gt;balise xml&lt;/a&gt;</xml>
  </entry>
  <entry>
    <nom>texte pour nom 3</nom>
    <valeur>texte pour valeur 3</valeur>
    <donnees>
      <debut>0</debut>
      <fin>100</fin>
    </donnees>
    <liste>
      <entry>liste 0</entry>
      <entry>liste 1</entry>
      <entry>liste 2</entry>
      <entry>liste 3</entry>
      <entry>liste 4</entry>
    </liste>
    <xml>donnees avec &lt;a&gt;balise xml&lt;/a&gt;</xml>
  </entry>
 
...
</entries>

Et voilà !

  • Share/Bookmark
jan 24

Dans ce rapide tutoriel, nous allons voir comment intégrer une image dans un code HTML.

En effet, cela peut-être pratique par exemple pour embarquer du contenu web dans un base de données.

Pour cela, il suffit d’utiliser la balise img, évidemment, ainsi que le codage base64.

En HTML, cela ressemble à ça :

<img src="data:image/png;base64,..." />

Voici un script bash qui permet d’encoder le tout automatiquement, grâce à openssl.

#!/bin/sh
 
if [ $# -eq 0 ]; then
	echo "Command: ${0} <image file>"
	exit 1
fi
 
path=$1
source=$(basename ${path})
extension=${source##*.}
base=${source%.*}
output="${base}.html"
base64=$(openssl base64 -in ${path})
 
echo "<html><head></head><body><img src=\"data:image/${extension};base64,${base64}\" /></body></html>" > $output
 
exit 0

Le script fonctionne ainsi :

  • ./monscript.sh monfichierimage.png
  • génération automatique de monfichierimage.html
  • Share/Bookmark
jan 06

Je profite de ce nouvel article sur le blog  de Kaeli Soft pour me présenter. Jérémie Veillet, jeune auto-entrepreneur de 26 ans, j’ai pour travail tout ce qui concerne la partie Recherche & Développement au sein de l’équipe. Je travail actuellement sur le site internet de la société, et sur différents moyens d’utiliser le qrcode au sein d’applications, mais sur cela nous reviendrons plus tard.

Je vais vous décrire en quelques étapes simples comment mettre en place sur votre site internet un champs de recherche Google personnalisé.

Prérequis : Avoir des connaissances de base en Javascript.

Tout d’abord il convient de créer une page HTML basique qui va nous servir de squelette pour afficher nos différentes recherches, appelons-la « recherche.html ». Elle contient pour le moment uniquement deux éléments div qui vont nous servir pour la suite.

<!doctype html>
<html>
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
	<title>Moteur de recherche Kaeli Soft</title>
</head>
<body>
	<!-- Conteneur des résultats -->
	<div id="searchResults">Chargement...</div>
 
	<!-- Conteneur des contrôles de navigation -->
	<div id="searchcontrol">Chargement...</div>
</body>
</html>

Ensuite, nous devons lier notre page aux API de Google à l’aide d’un script pour pouvoir utiliser les fonctions mises à disposition.

<script src="http://www.google.com/jsapi" type="text/javascript"></script>

C’est facultatif mais chaque développeur peut demander gratuitement une clé d’API aux services de Google, cela leur permet de tracer plus facilement s’il y a un problème dans le script exécuté.

Tentons d’afficher le champs de Recherche Google. Créons une fonction Onload() qui va contenir le code javascript nous permettant de faire nos traitements :

<!doctype html>
<html>
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
	<title>Moteur de recherche Kaeli Soft</title>
   	<script src="http://www.google.com/jsapi" type="text/javascript"></script>
    <script language="Javascript" type="text/javascript">//<![CDATA[
    // Chargement du champ de recherche google, les deux paramètres sont oligatoires
    // 1: nom de l'API à charger; 2: numéro de version de l'API (1.0)
    google.load('search', '1');
 
    // Fonction de traitement de la recherche
    function OnLoad() {
 
      // Créer un objet SearchControl qui permet de créer les contrôles basiques de la recherche
      var searchControl = new google.search.SearchControl();
      // WebSearch permet de limiter la recherche au Web (ou blogs, images, maps)
      searchControl.addSearcher(new google.search.WebSearch());
      // Affichage des champ de recherche
      searchControl.draw(document.getElementById("searchcontrol"));
 
       // Execute une recherche initiale
       searchControl.execute("Google");
    }
    // Appeler cette fonction pour que le traitement s'effectue une fois le document chargé
    google.setOnLoadCallback(OnLoad);
    //]]>
    </script>
</head>
<body>
	<!-- Conteneur des résultats -->
	<div id="searchResults">Chargement...</div>
 
	<!-- Conteneur des contrôles de navigation -->
	<div id="searchcontrol">Chargement...</div>
</body>
</html>

A cette étape vous devriez obtenir un champs de recherche et un résultat pour la recherche « google ». Bien sûr on peut initialiser la recherche pour qu’aucun résultat par défaut n’apparaisse, il suffit de mettre la méthode « execute » à vide.

Voyons maintenant, comment limiter la recherche à votre propre site et afficher plus de résultats sur la page. La méthode .setExpandMode permet de définir que l’on souhaite afficher l’intégrité des résultats (avec la constante EXPAND_MODE_OPEN), et la méthode .setRoot permet de forcer l’attachement des contôles de navigation sur le div souhaité (ici « searchcontrol »).

<!doctype html>
<html>
<head>
   <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
   <title>Moteur de recherche Kaeli Soft</title>
   <script src="http://www.google.com/jsapi?key=ABQIAAAA0hDneawcf7yozxJWOReHhxQPxJxl0OR9X1wz4qymITTzqEUA-RQWsCurC_KFwiw1_cM9Sw7_aU4peA" type="text/javascript"></script>
   <script language="Javascript" type="text/javascript">//<![CDATA[
    // Chargement du champ de recherche google, les deux paramètres sont oligatoires
    // 1: nom de l'API à charger; 2: numéro de version de l'API (1.0)
    google.load('search', '1');
 
    // Fonction de traitement de la recherche
    function OnLoad() {
 
      // Créer un objet SearchControl qui permet de créer les contrôles basiques de la recherche
      var searchControl = new google.search.SearchControl();
 
      // Définition des options du champ de résultats
      var options = new google.search.SearcherOptions();
      // Option permettant d'afficher tout les résultat trouvés, et des pages supplémentaires si les résultats sont nombreux
      options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
     // Attachement des contrôles à l'élément racine de la page qui possèdent l'id="searchcontrol"
     options.setRoot(document.getElementById("searchcontrol"));
 
    // Ajout d'un objet WebSearch pour préciser que l'on limite à la recherche web
    // BlogSearch pour la recherche sur les blogs
    // LocalSearch pour une recherche dans une localité
    // ImageSearch pour rechercher des images
     var siteSearch = new google.search.WebSearch();
    // Restrictions de recherche
    // setUserDefinedLabel: définit un label pour les résultats de recherche
     siteSearch.setUserDefinedLabel("kaelisoft.fr");
    // setUserDefinedClassSuffix: définit le suffixe des classes CSS des résultats (ex:gsc-resultsRoot-siteSearch)
     siteSearch.setUserDefinedClassSuffix("siteSearch");
    // setSiteRestriction: domaine du site sur lequel on veut chercher
     siteSearch.setSiteRestriction("kaelisoft.fr");
    // On applique les options de restrictions à la recherche
     searchControl.addSearcher(siteSearch, options);
 
    // Les résultats de recherche sont écrit dans l'élément qui possèdent l'id="searchResults"
     searchControl.draw(document.getElementById("searchResults"));
 
    // Execute une recherche initiale
     searchControl.execute("Google");
    }
    // Appeler cette fonction pour que le traitement s'effectue une fois le document chargé
    google.setOnLoadCallback(OnLoad);
    //]]>
    </script>
</head>
<body>
   <!-- Conteneur des résultats -->
   <div id="searchResults">Chargement...</div>
 
    <!-- Conteneur des contrôles de navigation -->
    <div id="searchcontrol">Chargement...</div>
</body>
</html>

Alors maintenant que l’on à fait ça, vous me direz, ce n’est pas forcément ce qu’il y à de plus beau, ou très adapté au design de votre site. Heureusement, Google à tout prévu, et il est possible d’entièrement customiser l’affichage, du champs de recherche aux résultats, par l’intermédiaire de feuille de style CSS.

Imaginons que l’on ne veuille afficher que le champ texte pour la recherche, pas de boutons « Recherche », ni de bouton pour effacer et surtout pas de logo « Fournis pas Google ». Pour connaitre facilement le nom des class à modifier, je conseille d’utiliser l’inspecteur d’éléments (Safari) ou d’utiliser le plugin Firebug pour Firefox (disponible ici).

Il faut alors ajouter dans l’entête du fichier html un petit peut de CSS:

<style type="text/css">
   .gsc-branding, 
   .gsc-title, 
   .gsc-stats, 
   .gsc-search-button, 
   .gsc-clear-button {
       visibility: hidden;
   }
</style>

Maintenant essayons de booster un peu ces champ de recherche en rendant le bouton rechercher noir et le champ de saisie avec des bords ronds.

.gsc-search-button{
   background-color: #000000;
   color: #ffffff;
   border: 1px solid #000000;
   /* Border-radius  */
   -moz-border-radius: 32px;
   -webkit-border-radius: 32px;
   border-radius: 32px;
}
 
input[type=text] {
   width: 140px;
   font-size: 13px;
   text-align: left;
   color: #000000;
   background-color: #ffffff;
   border: 1px solid #000000;
 
   /* Border-radius  */
   -moz-border-radius: 32px;
   -webkit-border-radius: 32px;
   border-radius: 32px;
}

vous pouvez télécharger le fichier complet ici : recherche.html

Ceci n’est qu’une méthode pour interagir avec les outils de rechercher Google, ici avec javascript, mais on peut aller encore plus avec PHP, Java, Python, mais ceci sera peut être l’objet d’un autre article.

  • Share/Bookmark