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

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


précédentsommairesuivant

II. ModeliXe

II-A. Présentation

Image non disponible

Le projet

ModeliXe est un projet français qui a été transmis à Rémy Trichard en 2004. La liste des contributeurs est longue et j'y ai retrouvé des noms que je connaissais : Sébastien Hordeaux (créateur de Waterpoof/PHPEdit) et Frédéric Bouchery (ancien bloggueur aux billets intéressants).

Après un long séjour sur le site personnel de RémyLe site officiel temporaire de ModeliXe, le projet a maintenant migré vers SourceForgeSite officiel de ModeliXe.

Installation

Arborescence du projet :
  • C:\Web\offline\shared\ModeliXe\ : La bibliothèque et son gestionnaire d'erreurs (ModeliXe.php et ErrorManager.php) ;
  • C:\Web\offline\sites\comparatifs\modelixe\ : La classe personnalisée et le script de configuration (MyModeliXe.php et Mxconf.php) ;
  • C:\Web\online\comparatifs\ : Les scripts à charger par le navigateur (modelixe-pear-nocache.php, modelixe-xml-nocache.php, modelixe-pear-cache.php et modelixe-xml-cache.php).

Avec la panoplie de contributeurs, je m'attendais à une installation sans douleur. Il n'en a malheureusement pas été ainsi...

Voici comment j'ai procédé : j'ai déclaré dans mon code les deux constantes conseillées, j'ai désactivé les vérifications automatiques et j'ai utilisé les constantes de manière explicite.

MyModeliXe.php
Sélectionnez
define('MX_GENERAL_PATH', realpath(dirname(__FILE__)).'/');
define('MX_ERROR_PATH', 'C:/Web/offline/shared/ModeliXe/');
ModeliXe.php - Original
Sélectionnez
//Initialisation de l'include_path
$os = ((stristr(getenv('SERVER_SOFTWARE'), 'win') || stristr(getenv('SERVER_SOFTWARE'), 'microsoft'))? ';' : ':');
$path = ((defined('MX_GENERAL_PATH'))? MX_GENERAL_PATH : '').$os.((defined('MX_ERROR_PATH'))? MX_ERROR_PATH : '').$os.ini_get('include_path');
ini_set('include_path', $path);

//Inclusion du fichier de configuration et de Error Manager
require_once('Mxconf.php');
require_once('ErrorManager.php');
ModeliXe.php - Correction
Sélectionnez
//Initialisation de l'include_path
//$os = ((stristr(getenv('SERVER_SOFTWARE'), 'win') || stristr(getenv('SERVER_SOFTWARE'), 'microsoft'))? ';' : ':');
//$path = ((defined('MX_GENERAL_PATH'))? MX_GENERAL_PATH : '').$os.((defined('MX_ERROR_PATH'))? MX_ERROR_PATH : '').$os.ini_get('include_path');
//ini_set('include_path', $path);

//Inclusion du fichier de configuration et de Error Manager
require_once(MX_GENERAL_PATH.'Mxconf.php');
require_once(MX_ERROR_PATH.'ErrorManager.php');

Depuis PHP 5.2, les expressions rationnelles classiques POSIX ne sont plus incluses qu'en PECL (afin de laisser la place à l'extension PCRE), ce qui rend indisponible la fonction ereg() utilisée par la classe de gestion d'erreurs de ModeliXe, ErrorManager. Il faut donc apporter la modification suivante :

ErrorManager::SetErrorOut() - Original
Sélectionnez
ereg('http://', $url)
ErrorManager::SetErrorOut() - Correction
Sélectionnez
preg_match('`http://`', $url)

Il est possible que tout le monde n'ait pas de problème avec la version originale de ModeliXe ; seulement, que j'aie rencontré un souci signifie que toutes les configurations ne supportent pas ModeliXe et qu'il est donc préférable d'apporter les modifications mentionnées ici. D'autre part, Julian Dolby serait de cet avis égalementConférence internationale PHP : Analyzable PHP...

Héritage de la classe

Voici la classe que je vous propose d'utiliser :

MyModeliXe.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.
<?php

//
// Constantes requises par ModeliXe
//
define('MX_GENERAL_PATH', realpath(dirname(__FILE__)).'/');
define('MX_ERROR_PATH', 'C:/Web/offline/shared/ModeliXe/');

require(MX_ERROR_PATH.'ModeliXe.php');

class MyModeliXe extends ModeliXe
{
    var $caching;

    function MyModeliXe($template, $file, $caching)
    {
        if($caching)
        {
            $this->caching = TRUE;
            parent::ModeliXe($file, '', '', 180);
        }
        else
        {
            $this->caching = FALSE;
            parent::ModeliXe($file, '', '', 0);
        }

        //
        // Le balisage du gabarit : "pear" ou bien "xml"
        //
        switch($template)
        {
            case 'pear':
            case 'xml':
                $this->SetMxFlagsType($template);
            break;

            default:
                $this->SetMxFlagsType('xml');
            break;
        }

        //
        // Configuration
        //
        $this->SetMxOutputType('xhtml');
        $this->SetMxTemplatePath(MX_GENERAL_PATH.'templates/'.$template.'/html/');
        $this->SetMxCachePath(MX_GENERAL_PATH.'templates/'.$template.'/cache/');
        $this->SetMxCompress('off');
        $this->SetMxModRewrite('off');
        $this->SetMxSignature('off');

        //
        // Initialisation
        //
        $this->SetModeliXe();
    }


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

?>

Le constructeur attend le nom du gabarit (dans mon exemple, il s'agit de "pear" ou bien "xml"), du nom du fichier de gabarit ("*.tpl") et enfin d'un booléen déterminant si l'on souhaite utiliser le cache.

Le code suivant permet de créer l'objet dans nos scripts :

modelixe.php
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<?php

define('MX_TEMPLATE', 'pear'); // 'pear' ou 'xml'
define('MX_CACHING', TRUE);

require('C:/Web/offline/sites/comparatifs/modelixe/MyModeliXe.php');

$template = new MyModeliXe(MX_TEMPLATE, 'home.tpl', MX_CACHING);

//
// Envoi de données au gabarit
//

$template->MxWrite();

?>

II-B. Utilisation

ModeliXe propose deux manières d'écrire les éléments du gabarit : en suivant le standard proposé par le groupe PEAR (Smarty) ou bien selon son format natif : XML. C'est pour cette raison que j'ai mis en place deux gabarits.

Je vous présenterai systématiquement les deux notations (les deux gabarits). Les tests sertont faits sur les deux versions également.

Les deux versions s'utilisent de la même manière dans le code PHP, grâce à notre classe dérivée.

Variables

Les variables sont envoyées dans des éléments "text" du gabarit au moyen de la méthode MxText().

header.tpl (XML)
Sélectionnez
    <meta name="generator" content="<mx:text id="meta_generator"/>" />
    <meta name="description" content="<mx:text id="meta_description"/>" />
    <meta name="keywords" content="<mx:text id="meta_keywords"/>" />
    <meta name="MS.LOCALE" content="<mx:text id="meta_mslocale"/>" />
header.tpl (PEAR)
Sélectionnez
    <meta name="generator" content="{text id="meta_generator"}" />
    <meta name="description" content="{text id="meta_description"}" />
    <meta name="keywords" content="{text id="meta_keywords"}" />
    <meta name="MS.LOCALE" content="{text id="meta_mslocale"}" />
header.php
Sélectionnez
$template->MxText('meta_generator', $template->getHtml($meta['generator']));
$template->MxText('meta_description', $template->getHtml($meta['description']));
$template->MxText('meta_keywords', $template->getHtml($meta['keywords']));
$template->MxText('meta_mslocale', $template->getHtml($meta['mslocale']));

Blocs

home.tpl (XML)
Sélectionnez
<mx:bloc id="subject">
    <div class="bloc_cours">
        <div class="titre_cours"><mx:text id="title"/></div>
    </div>
    <br />
    <br />
</mx:bloc id="subject">
home.tpl (PEAR)
Sélectionnez
{start id="subject"}
    <div class="bloc_cours">
        <div class="titre_cours">{text id="title"}</div>
    </div>
    <br />
    <br />
{end id="subject"}
home.php
Sélectionnez
$sql = 'SELECT id, title
        FROM subject';
$subjects = mysql_query($sql) or die(mysql_error());
while($subject = mysql_fetch_assoc($subjects))
{
    $template->MxText('subject.title', $template->getHtml($subject['title']));

    $template->MxBloc('subject', 'loop');
}

Imbrication

home.tpl (XML)
Sélectionnez
<mx:bloc id="subject">
    <div class="bloc_cours">
        <div class="titre_cours"><mx:text id="title"/></div>
    <mx:bloc id="category">
        <div class="categorie_cours"><mx:text id="title"/></div>
        <div class="liste_cours">
            <ul>
        <mx:bloc id="tutorial">
                <li>
                    <a href="<mx:text id="uri"/>"
                    ><mx:text id="title"/></a> : <mx:text id="description"/>
                </li>
        </mx:bloc id="tutorial">
            </ul>
        </div>
        <hr />
    </mx:bloc id="category">
    </div>
    <br />
    <br />
</mx:bloc id="subject">
home.tpl (PEAR)
Sélectionnez
{start id="subject"}
    <div class="bloc_cours">
        <div class="titre_cours">{text id="title"}</div>
    {start id="category"}
        <div class="categorie_cours">{text id="title"}</div>
        <div class="liste_cours">
            <ul>
        {start id="tutorial"}
                <li>
                    <a href="{text id="uri"}"
                    >{text id="title"}</a> : {text id="description"}
                </li>
        {end id="tutorial"}
            </ul>
        </div>
        <hr />
    {end id="category"}
    </div>
    <br />
    <br />
{end id="subject"}
home.php
Sélectionnez
$sql = 'SELECT id, title
        FROM subject';
$result = mysql_query($sql) or die(mysql_error());
while($subject = mysql_fetch_assoc($result))
{
    $template->MxText(
        'subject.title',
        $template->getHtml($subject['title']));

    $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))
    {
        $template->MxText(
            'subject.category.title',
            $template->getHtml($category['title']));

        $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))
        {
            $template->MxText(
                'subject.category.tutorial.uri',
                $template->getHtml($tutorial['uri']));

            $template->MxText(
                'subject.category.tutorial.title',
                $template->getHtml($tutorial['title']));

            $template->MxText(
                'subject.category.tutorial.description',
                $template->getHtml($tutorial['description']));

            $template->MxBloc('subject.category.tutorial', 'loop');
        }

        $template->MxBloc('subject.category', 'loop');
    }

    $template->MxBloc('subject', 'loop');
}

Alternance

Il nous faut utiliser la méthode déjà mentionnée ci-dessus du modulo :

home.tpl (XML)
Sélectionnez
                    <a href="<mx:text id="uri"/>"
                    ><mx:text id="title"/></a> :
                    <span style='color: <mx:text id="style"/>;'><mx:text id="description"/></span>
home.tpl (PEAR)
Sélectionnez
                    <a href="{text id="uri"}"
                    >{text id="title"}</a> :
                    <span style="color: {text id="style"};">{text id="description"}</span>
home.php
Sélectionnez
            if($i % 2)
            {
                $style = 'green';
            }
            else
            {
                $style = 'blue';
            }

            $template->MxText(
                'subject.category.tutorial.style',
                $style);

Segmentation du gabarit

ModeliXe a opté pour une solution similaire à celle de Smarty. En effet, le fichier de gabarit "maître" contient des blocs (en notation PEAR ou XML) qui indiquent à quel endroit il faut fusionner le gabarit externe :

home.tpl (notation PEAR) :
Sélectionnez
{start id="header"}{end id="header"}
home.tpl (notation XML) :
Sélectionnez
<mx:bloc id="header"></mx:bloc id="header">

Le code PHP doit ensuite savoir à quel endroit il peut trouver le gabarit à fusionner :

modelixe-pear-nocache.php
Sélectionnez
$template = new MyModeliXe(MX_TEMPLATE, 'home.tpl', MX_CACHING);
$template->MxBloc('header', 'modi', $template->mXTemplatePath.'header.tpl');
$template->MxBloc('footer', 'modi', $template->mXTemplatePath.'footer.tpl');

Le paramètre "modi" signifie que l'on souhaite remplacer le bloc du gabarit par un gabarit externe.

Les variables du gabarit fusionné sont décalées dans un espace de noms interne (le handle version ModeliXe), ici "header" :

 
Sélectionnez
$template->MxText('header.meta_generator', $template->getHtml($meta['generator']));
$template->MxText('header.meta_description', $template->getHtml($meta['description']));
$template->MxText('header.meta_keywords', $template->getHtml($meta['keywords']));
$template->MxText('header.meta_mslocale', $template->getHtml($meta['mslocale']));

Dans le gabarit, cette distinction est automatique. Seul le code PHP est affecté.

Gestion du cache

ModeliXe gère automatiquement le cache des information dynamiques du gabarit, il n'y a donc pas à trop s'en occuper.

Cependant, il ne peut pas éviter les appels à la base de données. Il peut seulement éviter de fusionner le gabarit et les variables PHP en réutilisant le fichier mis en cache. Cela n'évite pas les appels à la base de données, or c'est l'opération la plus coûteuse.

Il nous faut donc utiliser un système permettant de savoir si le fichier est déjà en cache. Selon le cas, nous nous connecterons à la base de données ou nous réutiliserons le document existant.

Cela se fait au moyen de la méthode MxCheckCache() :
Sélectionnez
if($template->MxCheckCache())
{
    //
    // Mettre ici les requêtes de récupération des données, l'envoi des données au gabarit, etc.
    //
}

Cet appel est le même pour tous les segments du document, puisqu'il n'y a qu'un seul fichier de cache pour tout le document final. Cela ne nous empêche pas de pouvoir appeler cette méthode dans chacun de nos scripts, si nécessaire.

ModeliXe ne produit qu'un seul fichier de cache qui est le document complet final. Il ne semble pas en mesure de gérer le cache par parties.

II-C. Mon avis

Avantages

De nombreuses personnes connues ont participé à l'élaboration de ModeliXe, ce qui donne un a priori favorable.

Il a une gestion du cache.

Inconvénients

La configuration de ModeliXe repose sur des constantes du scope global plutôt que sur des constantes de classes.

Le gabarit en XML est trompeur, dans la mesure où des variables doivent parfois être insérées dans le document à l'intérieur des propriétés des balises du gabarit : pourquoi écrire ces variables avec une syntaxe XML à cet endroit ?

Syntaxe XML douteuse :
Sélectionnez
<meta name="generator" content="<mx:text id="meta_generator"/>" />

Conclusion

ModeliXe me semble peu intéressant, dans la mesure où il n'apporte pas de nouveauté par rapport aux autres solutions, le support est à l'abandon et la communauté est inexistante.

Un résumé est disponible en fin d'article.


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.