Comparatif : Découverte des principaux moteurs de template en PHP

Smarty, phpBB2, TinyButStrong, VTemplate, ModeliXe
Image non disponible


précédentsommairesuivant

I. Introduction

I-A. Remerciements

Un grand merci à GrandFatherProfil : GrandFather pour ses remarques constructives, à hugo123Profil : hugo123 pour sa participation et sa patience ainsi qu'à eric190Profil : eric190 et dje3000Profil : dje3000 pour leur précieux soutien. Merci également aux auteurs et mainteneurs qui ont pris le temps de me corriger : Skrol29 (TinyButStrong), François Campana (VTemplate), Rémy Trichard (ModeliXe)...

I-B. Préambule

In the end you will always have an intersection point between presentation and logic.  There is no way around it since the final thing you present is a merge of the two.  The whole question of templating is about where you put this intersection point.
- Rasmus Lerdorf (créateur de PHP) dans la mailing list principale de PHP (http://www.php.net/).

C'est exactement ce qui transparaîtra de ce tutoriel : certains moteurs ont des gabarits passifs, tandis que d'autres déportent une grande partie de la logique applicative dans le fichier de gabarit.

Nous verrons les avantages et les inconvénients des deux méthodes (laisser le script PHP en charge de la logique ou bien la déporter dans le gabarit) au travers d'une exploration sommaire des principaux moteurs du marché.

GrandFather a porté mon attention sur une distinction faite couramment par les gens : un système de templates désigne les scripts construits selon l'idée de Rasmus (en PHP mais avec peu de code dans les fichiers HTML, il suffit donc d'exécuter le code qui peut être autonome), tandis que le moteur de templates met en place une méthode fondée sur une syntaxe spécifique (non PHP) qui doit donc être interprété par une classe PHP. Je me concentrerai ici sur cette seconde catégorie.

Cet article n'est pas un tutoriel complet sur chaque moteur. Au contraire, ce n'est qu'un survol de ce qu'il est possible de faire avec chacun d'eux en vue d'atteindre un objectif précis.

I-C. La démarche de comparaison

Objectif : prendre un document HTML et le reproduire à l'aide de chaque moteur de gabarit de manière à séparer la logique applicative de la présentation du document. Il s'agit de la page d'accueil de mon site sur Developpez.com. Cela permet de comparer les variables, les boucles et les imbrications, ce qui couvre l'ensemble de ce que je demande à un outil de ce style.

Cela ne permet pas de tester tous les aspects de chaque moteur mais je préfère me concentrer sur les fonctionnalités en commun et qui sont utiles pour l'objectif que je me suis fixé.

De mon point de vue, l'avantage des moteurs de gabarit réside dans la possiblité de distinguer le script PHP du code du document final (au format HTML ou tout autre format texte) : il s'agit de la fameuse séparation entre la logique et la présentation, fondamentale pour adopter une approche MVC ou pour répartir les tâches entre les intervenants dans un projet.

J'estime qu'un graphiste doit pouvoir se concentrer sur l'aspect visuel de la page Web sans devoir apprendre un langage de programmation pour y parvenir. À ce titre, le choix final sera amplement influencé par la simplicité de la syntaxe du gabarit.

Voici l'arborescence du projet :
  • C:\Web\offline\shared\ : Les bibliothèques téléchargées ;
  • C:\Web\offline\sites\comparatifs\ : Les classes personnalisées à inclure dans notre projet ainsi que les fichiers de gabarit et les fichiers de cache ;
  • C:\Web\online\comparatifs\ : Tous les éléments qui doivent être rendus publiques.
Les scripts du jeu de démo (sans moteur de gabarit) :
  • online\comparatifs\void-cache.php : Il affiche la version statique - HTML pur ;
  • online\comparatifs\void-nocache.php : Il inclut les scripts (cf. ci-dessous) qui génèrent la version dynamique - PHP et base de données ;
  • offline\sites\comparatifs\header.php : Il affiche l'en tête du document dynamique (quelques variables) ;
  • offline\sites\comparatifs\home.php : Il affiche le corps du document dynamique (quelques boucles) ;
  • offline\sites\comparatifs\footer.php : Il affiche le pied du document dynamique (rien de particulier).
void-nocache.php
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<?php

function html($string){
    return htmlentities($string, ENT_QUOTES, 'ISO-8859-1');
}

define('DIR_OFFLINE', 'C:/Web/offline/sites/comparatifs/void/');

include(DIR_OFFLINE.'home.php');

?>
home.php
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
<?php

require_once(realpath(dirname(__FILE__).'/../db-connect.php'));
include('header.php');

?>
<div id="alignement" style="text-align: center;">
    <a href="http://g-rossolini.developpez.com/" title="Cours par Guillaume Rossolini"
        ><img src="images/cours.png" alt="Les cours de Developpez.com" style="border: 1;" /></a>
    <br /><br />

    <?php
    $sql = 'SELECT id, title
            FROM subject';
    $subjects = mysql_query($sql) or die(mysql_error());
    while($subject = mysql_fetch_assoc($subjects))
    {
        ?>
        <div class="bloc_cours">
            <div class="titre_cours"><?php echo html($subject['title']); ?></div>
            <?php
            $sql = 'SELECT id, title
                    FROM category
                    WHERE subject_id = '.$subject['id'];
            $categories = mysql_query($sql) or die(mysql_error());
            while($category = mysql_fetch_assoc($categories))
            {
                ?>
                <div class="categorie_cours"><?php echo html($category['title']) ?></div>
                <div class="liste_cours">
                    <ul>
                        <?php
                        $sql = 'SELECT id, uri, title, description
                                FROM tutorial
                                WHERE category_id = '.$category['id'];
                        $tutorials = mysql_query($sql) or die(mysql_error());
                        while($tutorial = mysql_fetch_assoc($tutorials))
                        {
                            ?>
                            <li>
                                <a href="<?php echo html($tutorial['uri']); ?>"
                                ><?php echo html($tutorial['title']); ?></a> :
                                <?php echo html($tutorial['description']); ?>
                            </li>
                            <?php
                        }
                        ?>
                    </ul>
                </div>
                <hr />
                <?php
            }
            ?>
        </div>
        <br />
        <br />
        <?php
    }
    ?>

    <p id="validation">
        <a href="http://validator.w3.org/check?uri=referer"
            ><img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valide XHTML 1.1" /></a>
        <a href="http://jigsaw.w3.org/css-validator/check/referer"
            ><img src="http://jigsaw.w3.org/css-validator/images/vcss" alt="Valide CSS !" /></a>
    </p>
</div>
<?php

include('header.php');

?>
header.php
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
<?php

require_once(realpath(dirname(__FILE__).'/../db-connect.php'));


$result = mysql_query('SELECT name, value FROM header') or die(mysql_error());
while($header = mysql_fetch_assoc($result))
{
    $headers[$header['name']] = $header['value'];
}


$result = mysql_query('SELECT name, value FROM meta') or die(mysql_error());
while($tmp = mysql_fetch_assoc($result))
{
    $meta[$tmp['name']] = $tmp['value'];
}


$result = mysql_query('SELECT name, value FROM css') or die(mysql_error());
while($tmp = mysql_fetch_assoc($result))
{
    $css[$tmp['name']] = $tmp['value'];
}

?>
<?xml version="1.0" encoding="iso-8859-1"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
  <title><?php echo html($headers['title']); ?></title>
  <meta http-equiv="Content-Type" content="text/html; charset=<?php echo html($headers['charset']); ?>" />

  <meta name="generator" content="<?php echo html($meta['generator']); ?>" />
  <meta name="description" content="<?php echo html($meta['description']); ?>" />
  <meta name="keywords" content="<?php echo html($meta['keywords']); ?>" />
  <meta name="MS.LOCALE" content="<?php echo html($meta['mslocale']); ?>" />

  <link rel="stylesheet" type="text/css" href="<?php echo html($css['mainstyle']); ?>" />
  <link rel="stylesheet" type="text/css" media="print" href="<?php echo html($css['printer']); ?>" />
  <link rel="stylesheet" type="text/css" media="screen" href="<?php echo html($css['ridan']); ?>" />
</head>

...
void/footer.php
Sélectionnez
...

I-D. Consignes générales d'installation

J'ai souvent une manière très spéciale de configurer mon environnement... Je conçois que tout le monde n'adhère pas à ma vision des choses ; pourtant, si vous souhaitez suivre cet article et en utiliser les sources, il faudra vous plier à mon organisation.

J'utilise principalement trois répertoires :
  • C:\Web\offline\shared\ : Il centralise toutes les bibliothèques que j'utilise, ce qui me permet de les partager d'un site à l'autre ;
  • C:\Web\offline\sites\comparatifs\ : C'est le répertoire qui correspond aux scripts du site "comparatifs" qui ne doivent pas être accessibles par une URI : il s'agit généralement de scripts de configuration, de classes personnalisées ou de fichiers temporaires ;
  • C:\Web\online\comparatifs\ : C'est mon document_root, il contient toutes les ressources qui doivent être accessibles par une URI publique.

Vous pouvez aussi bien configurer votre document_root que modifier les scripts pour qu'ils acceptent votre configuration.

I-E. Informations générales sur les tests de performances

Les tests de performances sont effectués à l'aide du logiciel ApacheBench, fourni en standard avec httpd (Apache).

Les chiffres en eux-mêmes n'ont aucun sens, c'est pourquoi je donnerai toutes les valeurs relativement à une valeur de référence (appelée void) : le temps de chargement de la page de test. Je ne donnerai les logs produits par les tests qu'à titre informatif.

Ainsi, les valeurs des tests de performances sont des coefficients par rapport au temps d'exécution des scripts de référence (void).

Idéalement, les moteurs disposant d'une gestion du cache devraient produire des pages plus rapidement que le script de référence void-nocache.php (puisque ces moteurs évitent les appels à la base de données).

I-F. La base de données utilisée

Il serait peu cohérent de faire un test de performances de gabarits sans récupérer les informations depuis une base de données, surtout si le moteur gère la mise en cache des documents produits.

J'ai utilisé une base MySQL : pas de SQLite possible car il utilise le système de fichiers sans daemon/service (pour rappel, le système de fichiers est plus rapide qu'un daemon/service), ce qui fausse les statistiques.

Structure :
Sélectionnez
CREATE TABLE header
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(25) NOT NULL,
    value VARCHAR(255) NOT NULL
);

CREATE TABLE meta
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(25) NOT NULL,
    value VARCHAR(255) NOT NULL
);

CREATE TABLE css
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(25) NOT NULL,
    value VARCHAR(255) NOT NULL
);

CREATE TABLE subject
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL
);

CREATE TABLE category
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    subject_id INT UNSIGNED NOT NULL,
    title VARCHAR(255) NOT NULL
);

CREATE TABLE tutorial
(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    category_id INT UNSIGNED NOT NULL,
    uri VARCHAR(255) NOT NULL,
    title VARCHAR(255) NOT NULL,
    description VARCHAR(255) NOT NULL
);
Données :
Sélectionnez
INSERT INTO header
    (id, name, value)
VALUES
    (NULL, "charset", "ISO-8859-1"),
    (NULL, "title", "Guillaume Rossolini - Cours Web & PHP - Club d'entraide des développeurs francophones");

INSERT INTO meta
    (id, name, value)
VALUES
    (NULL, "generator", "developpez-com"),
    (NULL, "description", "Guillaume Rossolini - Cours Web & PHP"),
    (NULL, "keywords", ""),
    (NULL, "mslocale", "fr-FR");

INSERT INTO css
    (id, name, value)
VALUES
    (NULL, "mainstyle", "http://www.developpez.com/mainstyle-xhtml.css"),
    (NULL, "printer", "http://www.developpez.com/template/printer-xhtml.css"),
    (NULL, "ridan", "http://g-rossolini.developpez.com/ridan.css");

INSERT INTO subject
    (id, title)
VALUES
    (NULL, "Webmarketing"),
    (NULL, "PHP");

INSERT INTO category
    (id, title, subject_id)
VALUES
    (NULL, "Introduction", 1),
    (NULL, "Tutoriels", 1),
    (NULL, "Logiciels", 2),
    (NULL, "Tutoriels", 2),
    (NULL, "Reportages", 2);

INSERT INTO tutorial
(
    id, category_id,
    uri,
    title,
    description
)
VALUES
(
    NULL, 1,
    "http://g-rossolini.developpez.com/tutoriels/seo/",
    "Search Engine Optimization (SEO)",
    "introduction aux techniques de référencement d'une page Web"
),
(
    NULL, 2,
    "http://g-rossolini.developpez.com/tutoriels/seo/white-hat-techniques/",
    "Techniques de SEO dites de \"white hat\"",
    "vue d'ensemble des bonnes pratiques de SEO pour améliorer le référencement de vos pages"
),
(
    NULL, 2,
    "http://g-rossolini.developpez.com/tutoriels/seo/url-rewriting/",
    "Réécriture de liens (URL Rewriting)",
    "améliorez le référencement de vos pages avec cette technique"
),
(
    NULL, 2,
    "http://g-rossolini.developpez.com/tutoriels/seo/changer-d-url-rewriting/",
    "Faire évoluer sa réécriture de liens",
    "faites face à un changement dans votre politique de liens sans perdre votre référencement"
),
(
    NULL, 3,
    "http://g-rossolini.developpez.com/tutoriels/logiciels/phpedit-2-6/",
    "PHPEdit",
    "un IDE complet pour développer en PHP (coloration syntaxique, complétion de code, aide intégrée, nombreux plugins, etc.)"
),
(
    NULL, 4,
    "http://g-rossolini.developpez.com/tutoriels/logiciels/alterner-entre-plusieurs-php/",
    "Alterner entre plusieurs versions d'Apache et de PHP",
    "utilisez plusieurs versions de PHP pour tester vos scripts dans toutes les conditions"
),
(
    NULL, 4,
    "http://g-rossolini.developpez.com/tutoriels/php/expressions-regulieres/",
    "Les expressions régulières",
    "les expressions rationnelles (ou encore regex) expliquées avec des exemples concrets"
),
(
    NULL, 4,
    "http://g-rossolini.developpez.com/tutoriels/php/site-dynamique/",
    "Site dynamique et classes d''abstraction",
    "reprise du cours de Pierre-Baptiste pour utiliser un système de gabarits (templates) et PDO"
),
(
    NULL, 4,
    "http://g-rossolini.developpez.com/tutoriels/php/les-formulaires-et-php5/",
    "Les formulaires et PHP",
    "toutes les informations sur la gestion des formulaires en PHP"
),
(
    NULL, 5,
    "http://g-rossolini.developpez.com/reportages/conference-internationale-php-2006/",
    "Conférence Internationale PHP 2006",
    "à Frankfurt-Mörfelden (Allemagne)"
);

Il pourrait y avoir un travail supplémentaire sur les relations entre les tables, mais ce n'est pas notre propos ici. Cette base de données sera suffisante.

Par ailleurs, le script suivant est inclus par les divers scripts qui ont besoin de la base de données :

offline\sites\comparatifs\db-connect.php
Sélectionnez
1.
2.
3.
4.
5.
6.
<?php

mysql_connect('localhost', 'root', '') or die(mysql_error());
mysql_select_db('developpez') or die(mysql_error());

?>

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2006 Guillaume Rossolini. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.