V. TinyButStrong▲
V-A. Présentation▲
Le projet▲
TinyButStrongLe site officiel de TinyButStrong
L'idée avec TinyButStrong est de réutiliser autant que possible tout le code existant, que ce soit du PHP ou du HTML.
L'auteur, Skrol29, est quelqu'un de très ouvert et toujours disponible pour les utilisateurs de TBS. Je ne saurai trop le remercier pour sa patience après sa lecture de la première version de ce comparatif. C'est toujours un plaisir de voir qu'une communauté conserve son personnage moteur.
Skrol29 a minutieusement révisé toute la partie « TinyButStrong » de ce comparatif. J'ai conservé certains de mes commentaires, mais le côté technique est corrigé. Il ne subsiste pas d'erreurs.
Installation▲
Extraire le script tbs_class_php5.php comme vous en avez maintenant l'habitude (hors de la racine du serveur Web).
Arborescence du projet :
- C:\Web\offline\shared\TBS\ : La bibliothèque (tbs_class_php5.php) ;
- C:\Web\offline\shared\TBS\plug-ins\ : Le plugin de cache (tbs_plugin_cache.php) ;
- C:\Web\offline\comparatifs\tbs\ : La classe personnalisée (MyTinyButStrong.php) ;
- C:\Web\online\comparatifs\ : Les scripts à charger par le navigateur (tbs-nocache.php et tbs-cache.php).
Héritage de la classe▲
Voici la classe que je vous propose d'utiliser :
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.
<?php
require('C:/Web/offline/shared/TBS/tbs_class_php5.php'
);
class
MyTinyButStrong extends
clsTinyButStrong
{
public
$Caching
;
public
$_BasePath
;
public
$_PluginsPath
;
public
$_TemplatePath
;
public
$_CacheLifeTime
;
public
function
__construct
($template
,
$caching
)
{
parent
::
__construct
();
//
// Configuration de l'objet
//
$this
->
NoErr =
TRUE
;
$this
->
Caching =
$caching
;
$this
->
HtmlCharSet =
'ISO-8859-1'
;
$this
->
_ChrOpen =
'{$'
;
$this
->
_ChrClose =
'}'
;
$this
->
_ChrVal =
'{$val}'
;
$this
->
_BasePath =
dirname(__FILE__
);
$this
->
_PluginsPath =
'C:/Web/offline/shared/TBS/plug-ins/'
;
$this
->
_TemplatePath =
$this
->
_BasePath.
'/templates/'
.
$template
.
'/html/'
;
if
($this
->
Caching)
{
$this
->
_CachePath =
$this
->
_BasePath.
'/templates/'
.
$template
.
'/cache/'
;
$this
->
_CacheLifeTime =
180
;
// En secondes
//
// Installation et démarrage du plugin de cache
//
require_once($this
->
_PluginsPath.
'tbs_plugin_cache.php'
);
$this
->
PlugIn(TBS_INSTALL,
TBS_CACHE,
$this
->
_CachePath,
'*.cache'
);
}
else
{
$this
->
Render =
TBS_OUTPUT;
// TBS_NOTHING ou TBS_OUTPUT ou TBS_EXIT
$this
->
_CachePath =
NULL
;
$this
->
_CacheLifeTime =
NULL
;
// En secondes
}
}
public
function
LoadTemplate($file
,
$merge
=
FALSE
)
{
if
($merge
==
'+'
)
{
parent
::
LoadTemplate($this
->
_TemplatePath.
'/'
.
$file
,
'+'
);
}
else
{
if
($this
->
Caching)
{
$this
->
PlugIn(TBS_CACHE,
$file
,
$this
->
_CacheLifeTime);
}
parent
::
LoadTemplate($this
->
_TemplatePath.
'/'
.
$file
,
$this
->
HtmlCharSet);
}
}
}
?>
Le code suivant permet de créer l'objet dans nos scripts :
define('
DIR_OFFLINE
'
,
'
C:/Web/offline/comparatifs/tbs/
'
);
define('
TBS_TEMPLATE
'
,
'
default
'
);
define('
TBS_CACHING
'
,
FALSE);
require(DIR_OFFLINE.
'
MyTinyButStrong.php
'
);
$template
=
new MyTinyButStrong(TBS_TEMPLATE,
TBS_CACHING);
//
// Série d'appels à $temlate->LoadTemplate() suivis d'envoi de données au gabarit
//
$template
->
Show();
Le constructeur attend le nom du fichier de gabarit et un booléen déterminant si l'on souhaite utiliser le cache.
Notez que j'ajoute une propriété $_TemplatePath à la classe. Je la laisse en accès public par souci d'homogénéité avec la classe de départ, mais, si cela n'avait tenu qu'à moi, elle aurait été protected.
J'ai également apporté une amélioration afin d'éviter de devoir répéter le chemin d'accès au gabarit à chaque chargement de fichier de gabarit.
En temps normal, la méthode LoadTemplate() s'adapte automatiquement : la première utilisation définit le répertoire des gabarits, puis les utilisations suivantes peuvent se contenter de donner le nom du fichier de gabarit sans évoquer le chemin d'accès complet.
Je pars ici du principe que seul le premier appel à LoadTemplate() construit le gabarit à partir de rien, tous les autres étant a priori pour y ajouter du code HTML, c'est pourquoi ces appels ont le paramètre '+' lorsque la propriété Source est non vide.
Enfin, TBS permet de configurer les marqueurs de début et de fin de bloc. J'ai pris le parti de les changer. Par défaut, TBS utilise des crochets [], or je commence à m'habituer aux accolades et au dollar {$} des autres moteurs, j'ai donc utilisé ces caractères.
TBS peut configurer ces marqueurs de plusieurs manières. J'ai adopté ici la méthode la plus explicite, mais le constructeur peut le faire également.
V-B. Utilisation▲
Variables▲
TBS sait lier automatiquement les variables locales PHP avec le gabarit. Pour cela, il faut que les variables du gabarit respectent une syntaxe spéciale : {$var.nom_de_la_variable}.
Cependant, cela demanderait des manipulations peu pratiques et peu explicites. Il est préférable d'utiliser la méthode MergeField(), dont les intentions sont limpides.
$template
->
MergeField('
variable_du_gabarit
'
,
'
valeur_php
'
);
$result
=
mysql_query('
SELECT name, value FROM header
'
) or die(mysql_error());
while($meta
=
mysql_fetch_assoc($result
))
{
$template
->
MergeField('
meta_
'
.
$meta
[
'
name
'
],
$meta
[
'
value
'
]
);
}
<
meta name=
"
generator
"
content=
"
{
$meta_generator
}
"
/>
<
meta name=
"
description
"
content=
"
{
$meta_description
}
"
/>
<
meta name=
"
keywords
"
content=
"
{
$meta_keywords
}
"
/>
<
meta name=
"
MS.LOCALE
"
content=
"
{
$meta_mslocale
}
"
/>
Le comportement par défaut de TBS est de convertir les valeurs en entités HTML à l'aide de la fonction htmlspecialchars().
Blocs▲
TinyButStrong a une manière très particulière de gérer les répétitions de portions de gabarit, qu'il appelle « blocks ». La méthode MergeBlock() s'adapte en fonction des paramètres qui lui sont passés, par exemple ici une ressource MySQL ou un tableau de valeurs :
$subjects
=
array();
$result
=
mysql_query($sql
[
'
subjects
'
]
);
while($subject
=
mysql_fetch_assoc($result
))
{
$sujects
[]
=
$subject
;
}
$template
->
MergeBlock('
subject
'
,
$subjects
);
$template
->
MergeBlock('
subject
'
,
$resource
,
'
SELECT id, title FROM subject
'
)
{
$subject
;
block=
begin}
<
div class=
"
bloc_cours
"
>
<
div class=
"
titre_cours
"
>{
$subject
.
title}</
div>
</
div>
<
br />
<
br />
{
$subject
;
block=
end}
Dans cet exemple, TBS fusionne les données directement depuis la requête SQL. Il faut bien se rappeler que, selon les bonnes règles de design d'une application, les requêtes SQL devraient retourner des informations prêtes à être affichées (hors conversion vers le charset de destination). Il ne devrait donc pas être nécessaire de récupérer les données avant de les envoyer au gabarit.
Imbriquation▲
$sql
=
'
SELECT id, title
FROM subject
'
;
if($template
->
MergeBlock('
subject
'
,
$resource
,
$sql
) >
0
)
{
$sql
=
'
SELECT id, title
FROM category
WHERE subject_id = %p1%
'
;
if($template
->
MergeBlock('
category
'
,
$resource
,
$sql
) >
0
)
{
$sql
=
'
SELECT id, uri, title, description
FROM tutorial
WHERE category_id = %p2%
'
;
$template
->
MergeBlock('
tutorial
'
,
$resource
,
$sql
);
}
}
if($template
->
MergeBlock('
subject
'
,
'
array
'
,
'
subjects
'
) >
0
)
{
if($template
->
MergeBlock('
category
'
,
'
array
'
,
'
subjects[%p1%][categories]
'
) >
0
)
{
$template
->
MergeBlock('
tutorial
'
,
'
array
'
,
'
subjects[%p1%][categories][%p2%][tutorials]
'
);
}
}
{
$subject
;
block=
begin}
<
div class=
"
bloc_cours
"
>
<
div class=
"
titre_cours
"
>{
$subject
.
title}</
div>
{
$category
;
block=
begin;
p1={
$subject
.
id}}
<
div class=
"
categorie_cours
"
>{
$category
.
title}</
div>
<
div class=
"
liste_cours
"
>
<
ul>
{
$tutorial
;
block=
begin;
p1={
$subject
.
id};
p2={
$category
.
id}}
<
li>
<
a href=
"
{
$tutorial
.uri
}
"
>{
$tutorial
.
title}</
a>
:
{
$tutorial
.
description}
</
li>
{
$tutorial
;
block=
end}
</
ul>
</
div>
<
hr />
{
$category
;
block=
end}
</
div>
<
br />
<
br />
{
$subject
;
block=
end}
TBS utilise des variables automatiques dans le code PHP (p1, p2 p3, etc.) pour relier les blocs imbriqués. Cette variable est remplacée à chaque itération : on utilise habituellement une variable issue du bloc parent.
Le fichier de gabarit construit cette variable dans la définition du block, tandis que le script PHP l'utilise pour filtrer les données.
Le secret de l'imbrication des boucles repose dans ces variables automatiques.
NB : Le dernier extrait de code PHP correspond au tableau hiérarchique construit lors de l'utilisation du moteur Smarty, dans le script home.php.
Alternance▲
Il suffit de répéter le block dans le gabarit, ce qui oblige TBS à utiliser une alternativement tous les blocks.
{
$subject
;
block=
begin}
<
div class=
"
bloc_cours
"
>
<
div class=
"
titre_cours
"
>{
$subject
.
title}</
div>
{
$category
;
block=
begin;
p1={
$subject
.
$}}
<
div class=
"
categorie_cours
"
>{
$category
.
title}</
div>
<
div class=
"
liste_cours
"
>
<
ul>
{
$tutorial
;
block=
begin;
p1={
$subject
.
$};
p2={
$category
.
$}}
<
li>
<
a href=
"
{
$tutorial
.uri
}
"
>{
$tutorial
.
title}</
a>
:
<
span style=
"
color: green;
"
>{
$tutorial
.
description}</
span>
</
li>
{
$tutorial
;
block=
end}
{
$tutorial
;
block=
begin}
<
li>
<
a href=
"
{
$tutorial
.uri
}
"
>{
$tutorial
.
title}</
a>
:
<
span style=
"
color: blue;
"
>{
$tutorial
.
description}</
span>
</
li>
{
$tutorial
;
block=
end}
</
ul>
</
div>
<
hr />
{
$category
;
block=
end}
</
div>
<
br />
<
br />
{
$subject
;
block=
end}
Segmentation du gabarit▲
Comme très souvent avec TinyButStrong, il y a plusieurs manières de procéder.
L'approche décrite dans la documentation (dans la section subtemplates) permet d'avoir une gestion similaire à celle proposée par Smarty : le gabarit principal (home.tpl) définit les gabarits à inclure lors de son chargement par l'objet TBS. Dans approche, TinyButStrong permet d'exclure ce qui se trouve à l'extérieur de la balise <body> (configurable avec getbody=balise), ce qui est utile lorsque l'on utilise des sous-gabarits construits par un outil comme Dreamweaver.
Une autre approche consiste à charger les gabarits les uns après les autres par la méthode LoadTemplate() :
$template
=
new MyTinyButStrong(TBS_TEMPLATE,
TBS_CACHING);
// Chargement des différents gabarits dans le moteur
$template
->
LoadTemplate('
header.tpl
'
);
$template
->
LoadTemplate('
home.tpl
'
,
'
+
'
);
$template
->
LoadTemplate('
footer.tpl
'
,
'
+
'
);
//
// Mettre ici l'envoi des données au moteur
//
// Affichage de la page finale
$template
->
Show();
Gestion du cache▲
TBS dispose d'un plugin (CacheSystem, inclus en fourniture standard) permettant de gérer la mise en cache du document produit.
require_once('
tbs_plugin_cache.php
'
);
$template
->
PlugIn(TBS_INSTALL,
TBS_CACHE,
$template
>
_CachePath,
'
*.cache
'
);
$template
->
PlugIn(TBS_CACHE,
'
file.tpl, $template->_CacheLifeTime);
Dans la mesure où TinyButStrong se charge des requêtes à la base de données, il n'est pas nécessaire de vérifier la validité du cache comme avec les autres moteurs de gabarit. En effet, TBS n'effectue pas de requête s'il a déjà les résultats en cache.
V-C. Mon avis▲
Avantages▲
PHP 4 et 5.
Possibilité d'installer et de développer des plugins.
La syntaxe « relative » du gabarit peut sembler déroutante, mais en réalité elle est ingénieuse : elle permet de créer des gabarits XML depuis l'intérieur d'une application comme Office2007 ou OpenOffice. D'ailleurs, un plugin OpenOffice existe pour TBS…
TBS est prévu pour utiliser des gabarits 100% compatibles W3C, ce qui permet à un graphiste de créer les documents avec son outil favori.
Inconvénients▲
Cela relève du commentaire personnel, mais je n'adhère pas toujours à la manière de coder, notamment l'utilisation de constantes dans le scope global plutôt qu'à l'intérieur d'une classe (groupement de classes dans un même script, un même paramètre ou une même méthode peut avoir diverses utilisations).
Construire des requêtes SQL n'est pas la vocation de TBS (et j'adhère à ce refus), mais la classe permet néanmoins d'exécuter des requêtes. Cependant, aucune protection n'est appliquée aux requêtes SQL envoyées par MergeBlock() ; il appartient au développeur de le faire avec la bonne fonction : par exemple pour MySQL, il s'agit de mysql_real_escape_string().
Conclusion▲
TBS est le moteur de gabarits qu'il m'a été le plus difficile de maîtriser. J'ai finalement réussi à atteindre mon objectif (reproduire le document initial) : je le dois à l'aide patiente de son auteur, Skrol29.
Un résumé est disponible en fin d'article.