Affichage des articles dont le libellé est veille. Afficher tous les articles
Affichage des articles dont le libellé est veille. Afficher tous les articles

18 novembre 2008

Comment traiter du JSON pour produire du HTML en Javascript ?

Lors d'un développement de type AJAX, on se trouve rapidement confronté à la problématique suivante :

  • Je récupère des données au format JSON
  • Je les affiche en HTML

Question toute bête à priori : mais comment faire cela ?

Old School

On parcourt la structure Javascript et on produit le HTML avec des fonctions comme document.write ou element.appendChild

Exemple :


var data = [ "Meuse",   "Vosges",   "Moselle",   "Meurthe & Moselle" ];

document.write('<ul>');
for(i = 0; i < data.length; i++) {
    document.write('<li>'+data[i]+'</li>');
}
document.write('</ul>');

ou


<ol id="list">
</ol>
        
ol = document.getElementById("list");
for(i = 0; i < data.length; i++) {
    li = document.createElement("li");
    li_text = document.createTextNode(data[i]);
    li.appendChild(li_text);
    ol.appendChild(li);
}

Temps modernes : les frameworks

Bien sûr, maintenant, plus personne n'écrit du Javascript sans utiliser des frameworks comme Prototype ou JQuery.

Cependant, on écrira la même chose qu'auparavant de manière plus simple :

Avec Prototype :


<ol id="list3"> </ol>

data.each(function(item) {
    $('list3').insert(new Element('li').update(item));
});

Avec JQuery :


<ol id="list2"> </ol>


jQuery.each(data, function(i,item){
    jQuery("#list2").append(jQuery("<li>"+item+"</li>"));
});

On remarque ici la présence presque obligatoire d'une balise html d'ancrage. (ici, la balise &lt;ol id="listX"&gt; )

Le futur : les templates

A l'usage, les méthodes précédentes posent évidement le problème de la séparation du fond et de la forme : un problème bien connu en PHP et plus généralement pour la conception de site web traditionnel. (cf l'article Wikipedia sur les templates)

Alors nous voilà revenu au temps du choix d'un moteur de template !

Pour faire ma sélection dans cette liste, il y a plusieurs critères :

  • une preuve de vie (évolution des versions par exemple, activité sur le site)
  • une (bonne) documentation à jour
  • un fonctionnement multi- browser
  • une capacité à traiter des structures de données complexes

J'ai choisi de comparer :

  • JSrepeter
  • jstemplate
  • Pure

Pour chacun d'entre eux, j'essayerai de traiter le tableau de données suivant :


var data = {
   "items":[
      {
         "titre":"titre 1",
         "img":"http:\/\/localhost\/images\/01.jpg",
         "list1":[
            "row 1", 
            "row 2"
         ],
         "list2":[
            {
               "nom":"nom 1"
            },{
               "nom":"nom 2"
            }
         ]
      },
      {
         "titre":"titre 2",
         "img":"http:\/\/localhost\/images\/02.jpg",
         "list1":[
            "row A", 
            "row B"
         ],
         "list2":[
            {
               "nom":"nom A"
            },{
               "nom":"nom B"
            }
         ]
      }
   ]
}

Compiler un template

Avec jsrepeter :


$('#test').fillTemplate(data);

Avec jstemplate :


var input = new JsEvalContext(data);
var output = document.getElementById('test');
jstProcess(input, output);

Avec pure :


$('#test').autoRender(data);

Afficher une valeur dans une balise ou l'attribut d'une balise

Avec jsrepeter :


<ol id="test">
    <li context='items' >
        <img src="${img}" />
        <span>${titre}</span>
    </li>
</ol>

Avec jstemplate :


<ol id="test">
    <li jsselect="items" >
        <img jsselect="img" jsvalues="src:$this" />
        <span jscontent="titre">xxx</span>
    </li>
</ol>

Avec pure :


<ol id="test">
    <li class="items">
        <img class="img@src" src="xxx" />
        <span class="titre">XXX</span>
    </li>
</ol>        
                                                                                

Traiter les sous listes avec un index numérique

Avec jsrepeter :

Impossible à faire ?
En tout cas pour le moment, je n'ai pas réussi.

Avec jstemplate :


<ol id="test">
    <li context='items' >
        <ol>
        <li jsselect="list1" jscontent="$this">XXX</li>
        </ol>
    </li>
</ol>

Avec pure :

En théorie (cf .démo) cela semble possible, mais la syntaxe est loin d'être triviale, j'abandonne pour le moment.

Traiter les sous listes avec un index alphanumérique

Avec jsrepeter :


<ol id="test">
    <li context='items' >
        <ol>
        <li context='list2'>${nom}</li>
        </ol>
    </li>
</ol>

Avec jstemplate :


<ol id="test">
    <li context='items' >
        <ol>
        <li jsselect="list2"  jscontent="nom">XXX</li>
        </ol>
    </li>
</ol>

Avec pure :
En théorie (cf .démo) cela semble possible, mais la syntaxe est loin d'être triviale, j'abandonne pour le moment.

Conclusion

A ce jour, pour moi, le meilleur système de template est jstemplate, car il est capable de traiter n'importe quel format de données tout en restant très simple à l'usage.

Malgré une apparente facilité, Pure est compliqué à utiliser. De plus, l'usage de l'attribut class prête à confusion et rend le template au final moins lisible.

Jsreapter possède la syntaxe la plus simple et il est surement le plus facile à appréhender. Malheureusement, il semble incapable de traiter les index numériques...

10 octobre 2008

Les performances des systèmes de cache en PHP en question.

Les sites WEB se doivent d'être rapide. Dans le cas contraire, le développeur va chercher à optimiser son application et il peut être tenté par des systèmes de cache. Mais quel système de cache choisir ? Sont-il vraiment efficaces ?

Lieu de stockage

Le principal critère de sélection d'un mécanisme de cache est son type de stockage. En voici 3 :

  • Système de fichiers
  • Mémoire partagée
  • Base de données

Pour chaque type, il existe plusieurs logiciels ou
techniques permettant de réaliser le stockage. Pour le système de fichiers, on trouve différents formats de fichier. Pour la mémoire partagée, on trouve : APC, Xdebug, Memcached. Et pour les bases de données, on trouve : Sqlite, Mysql, etc ...

Implémentation en PHP

Pour chaque méthode de stockage et pour obtenir un mécanisme de cache, il est nécessaire d'encapsuler les primitives PHP. On trouve de nombreuses API réalisant ce travail.

A l'heure actuelle, Le Zend Framework fournit une couche d'abstraction pour la majorité des méthodes de stockage citées ci-avant. C'est donc un bon moyen de tester la pertinence et la rapidité des différents types de stockage.

Il est difficile d'évaluer l'impact des différentes implémentations en PHP d'un moteur de cache. Il n'est donc pas question ici de les comparer.

Code à tester

Pour évaluer les performances des différentes méthodes de stockage, on va utiliser 2 séries.

Série 1 : 1 * 100000

La première série stocke en cache une seule information d'une taille importante. Par exemple avec le Zend_Framework, on aura le code suivant
:


$cache = Zend_Cache::factory('Core', $backend, $frontendOptions,
$backendOptions);

if (!($data = $cache->load($id))) {

   $data = '';
   for ($i = 0; $i < 100000; $i++) {
       $data .= '. ';
   }

   $cache->save($data);
   echo '<h1>SAVED</h1>';
}
else echo '<h1>CACHED</h1>';

echo '<div>'.$data.'</div>';

Série 2 : 1000 * 100

La deuxième série stocke en cache plusieurs informations de petite taille mais pour un volume total identique à la première série. Par exemple avec le Zend_Framework, on aura le code suivant :


$cache = Zend_Cache::factory('Core', 'APC', $frontendOptions,
$backendOptions);

$c = false;
$datas = '';
for ($j = 0; $j < 1000; $j++) {
   $id = 'ID'.$j;
   if (!($data = $cache->load($id))) {

       $data = '';
       for ($i = 0; $i < 100; $i++) {
           $data .= '. ';
       }

       $cache->save($data, $id);
       $c = true;
   }
   $datas .= $data;
}
if ($c)
   echo '<h1>SAVED</h1>';
else
   echo '<h1>CACHED</h1>';

echo '<div>'.$datas.'</div>';

Banc d'essais

Pour évaluer les performances, on utilisera en parallèle : ab, httperf
et siege. Ces 3 logiciels permettent de simuler une charge importante sur un serveur WEB tout en mesurant les temps de réponses.

Chacun de ces logiciels est lancé séquentiellement 4 fois, à chaque fois on calcul le nombre moyen de requêtes par seconde.

La comparaison portera sur :

  • Zend_Cache, backend : FILE
  • Zend_Cache, backend : APC
  • Zend_Cache, backend : SQLITE
  • Pxxo 5.4
  • Pxxo 5.4.1

Résultats

Les chiffres sont obtenus en faisant la médiane des 4 moyennes du nombre de requête par seconde. Le tout étant arrondi par défaut.

Série 1 : 1 * 10000 (sans APC)

ab httperf siege
php (sans cache) 20 11 22
pxxo (5.4.1) 208 111 216
pxxo (5.4) 265 136 127
zend_cache (apc) - - -
zend_cache (file) 153 84 156
zend_cache (sqlite) 129 69 134

Série 1 : 1 * 100000 (avec APC)

ab httperf siege
php (sans cache) 22 11 21
pxxo (5.4.1) 464 210 424
pxxo (5.4) 436 202 402
zend_cache (apc) 361 178 344
zend_cache (file) 295 144 278
zend_cache (sqlite) 136 109 208

Série 2 : 1000 * 100 (sans APC)

ab httperf siege
php (sans cache) 21 10 21
pxxo (5.4.1) 8 5 9
pxxo (5.4) 6 3 6
zend_cache (apc) - - -
zend_cache (file) 3 1 3
zend_cache (sqlite) 6 3 6

Série 2 : 1000 * 100 (avec APC)

ab httperf siege
php (sans cache) 22 11 21
pxxo (5.4.1) 45 22 44
pxxo (5.4) 8 4 8
zend_cache (apc) 29 15 29
zend_cache (file) 3 1 3
zend_cache (sqlite) 6 3 6

Conclusion

Comme d'habitude, tout va dépendre de votre contexte. Cependant, on
peut tirer quelques enseignements de ce ban d'essai.

  • Tout d'abord, il est efficace d'utiliser un système de mise en cache pour éviter un traitement long. Mais, plus on va utiliser le système de mise en cache, moins il devient intéressant de l'utiliser.
  • Ensuite, ce test confirme que c'est toujours mieux d'utiliser APC ou du moins de l'activer. Cependant, APC n'est pas toujours disponible et il convient de trouver une solution de rechange.
  • Si le besoin de mise en cache se limite à une seule information, vous pouvez utiliser Zend_Cache avec un stockage sous forme de fichiers (FILE). L'utilisation du backend APC vous apporte un peu plus mais ce n'est même pas obligatoire.
  • Plus vous souhaitez utiliser le stockage en cache, moins vous devez

utiliser le stockage par fichier et plus l'utilisation d'APC devient
obligatoire. Sans APC, SQLITE est une bonne solution de repli.

Le cas Pxxo

Pxxo utilise intensément la mise en cache. Depuis l'origine, le but était de pouvoir limiter le nombre de traitement d'un appel sur
l'autre et le tout de manière presque transparente pour le développeur.

Pxxo, jusqu'à la version 5.4, a toujours utilisé des mécanismes de cache tierces (Cache_Lite de PEAR au début puis Zend_Cache ensuite).
Maintenant, Pxxo utilise son propre mécanisme. Le but était d'améliorer les performances en mode non APC. Cette petite étude permet de confirmer que le but est atteint.

Code source du test

http://www.touv.fr/IMG/zip/cachesystem-2.zip

30 avril 2008

Les différentes méthodes d’accès à un SGBD en PHP

Le but de ce billet est de faire un rapide état des lieux des différentes méthodes permettant, en PHP, d'interroger un Système de Gestion de Base de Données (SGBD)

Le choix du SGBD ne sera pas abordé ici. Les différentes méthodes présentées peuvent en théorie être utilisée indifféremment que l'on utilise que soit Oracle, MySql ou encore Sqlite...

La liste que je propose n'est pas forcement exhaustive, mais elle présente au moins toute les méthodes que je connais de près ou de loin.

Les Fonctions natives

Plusieurs SGBD possèdent une interface native sous forme de fonctions PHP. Ces fonctions permettent d'utiliser simplement le "driver" d'accès fourni par chaque SGBD. Une liste des SGBD supporté par PHP est disponible dans la documentation officielle de PHP

Le principe avantage est de pouvoir utiliser au mieux le SGBD, mais c'est aussi un inconvénient car on devra réécrire son code dans le cas ou l'API cliente évolue ou si l'on souhaite changer de SGBD. Pour MySQL, il existe 2 APIs (MySQL et mysqli). Pour Oracle, l'API en est à sa troisième version majeure (ORA, OCI8 et maintenant OCI), chaque version étant incompatible avec la précédente.

Si la majorité de ses fonctions natives sont des interfaces procédurales. Certaines APIs sont orienté objet. C'est la cas par exemple pour mysqli.

Les couches d'abstraction

MDB2

  • Couche d'abstraction d'accès à un SGBD.
  • Interface orienté objet.
  • En standard dans PEAR.

MDB2 utilise les fonctions PHP natives propres à chaque SGBD. On notera que MDB2 détecte automatiquement la structure des tables mais visiblement sans notion de Cache. Autre point MDB2 permet la création de table de données sans se soucier du SGBD. Cette fonctionnalité peut être intéressant si l'on souhaite déployer un schéma quand on ne connait pas le type du SGBD qui sera utilisé.

Exemple

require_once 'MDB2.php';
$mdb2 =&
MDB2::connect('mysql://root:secret@localhost/test');
if (PEAR::isError($mdb2)) {
  die($mdb2-&gt;getMessage());
}
$res = $mdb2-&gt;
query('SELECT num, nom FROM departement ');
while (($row = $res-&gt;fetchRow(MDB2_FETCHMODE_ASSOC))) {
  echo $row['num'] .'-'.$row['nom'] "\n";
}

PDO

  • Couche d'abstraction d'accès à un SGBD
  • Interface orienté objet
  • En standard dans PHP

Contrairement à toute les autres, PDO est écrit en C, et donc fourni de manière native dans PHP.

Exemple


require_once 'MDB2.php';
$db = new PDO('mysql:host=localhost;dbname=test', 'root', 'secret');
$res = $mdb2-&gt;query('SELECT num, nom FROM departement ');
while (($row = $res-&gt;fetchRow(MDB2_FETCHMODE_ASSOC))) {
  echo $row['num'] .'-'.$row['nom'] "\n";
}

ADODB

à approfondir ...

Les Object Relational Mapping (ORM)

Il existe plusieurs solutions d'ORM en PHP. Pour mettre à disposition du développeur des objets de manipulations des données, les systèmes d'ORM doivent connaitre le schéma de la base. Pour cela il existe plusieurs techniques :

* détection automatiquement à l'exécution avec éventuellement une mise en cache
* en différé avec un script qui va générer soit des fichiers de description en XML soit directement des fichiers PHP
* à la main...

Au final, l'ajout, la suppression, la modification d'une ligne de données pourra ressembler à ceci :

$car = new Car;
$data = array(
   'model' => 'Super Trooper',
   'hp'    => 140,
   'color' => 'black',
   'clima' => 0,
   'price' => 19000

);
$newCarId = $car->save($data);

Zend_Db

  • Couche d'abstraction d'accès à un SGBD.
  • Interface orienté objet
  • ORM
  • En standard dans le Zend Framework

Zend_DB se décompose en plusieurs sous modules. Zend_DB propose à priori la même chose que MDB2 mais il lui ajoute la couche ORM. A noter que derrière les objets PHP on retrouve le module PDO. Comme MDB2, Zend_Db devine les structres de données mais visiblement cette opération peut être mis en cache.

Propel

  • En standard dans le Framework Symphony

jDao

  • En standard dans le Framework Jelix (Ne peut pas s'utliser en dehors)
  • Description des tables à écrire en XML

MDB_QueryTool

  • Couche d'abstraction d'accès à un SGBD
  • Interface orienté objet
  • ORM simplifié
  • Fourni par PEAR

DB_DataObject

  • ORM simplifié
  • Fourni par PEAR

PMO

à approfondir ...

Doctrine

à approfondir ...

AutoCRUD

à approfondir ...

REST et Radar

On peut également décider d'architecturer son application suivant le concept nommé
RADAR. Le principe veut que l'application soit construite autour d'une API de type REST.

La communication avec le SGBD se fera donc en s'appuyant sur des APIs REST. Ce type d'APIs peut être entièrement spécifique à l'application mais on peut aussi utiliser des APIs prête à l'emploi. En voici quelles unes :