Gestion d'un système de templates en PHP

Adrien Pellegrini

Création et explication d'un système de templates en PHP avec gestion de constantes avec ou sans boucle. ♪

- Titre : Gestion d'un système de templates en PHP

- Auteur : Adrien Pellegrini

- Parution : 24 août 2006

- Licence : Copyright ® 2006 Adrien Pellegrini. 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.

Sommaire

- I. Introduction

- I-A. Remerciements

- I-B. Problématique

- I-C. Qu'est-ce qu'un système de templates ?

- II. Exemple : test.php et test.tpl

- II-A. Simples variables

- II-B. Variables dans une boucle

- III. La classe Template

- III-A. Sa structure

- III-B. Ses méthodes

- Méthode __construct()

- Méthode simpleVar()

- Méthode loopVar()

- Méthode constantReplace()

- Méthode parse()

- IV. Méthode(s) supplémentaire(s)

- IV-A. Méthode includeFile()

- V. Conclusion

- V-A. Épilogue

- V-B. Liens

- VI. Annexe

- VI-A. Structure de la classe (code PHP)

- VI-B. Structure avancée de la classe

- VI-C. Structure du tableau $InfoTpl

I - Introduction

I-A - Remerciements

Tous mes remerciements à Yogui pour sa relecture.

I-B - Problématique

Ce tutoriel nécessite PHP 5

Ce tutoriel a pour but de vous donner une piste sur la création d'un système de templates.

Je vous conseille de le lire une fois tout en entier.

Ceci pour plusieurs raisons :

1. Il est question de créer une classe qui permettra la gestion de templates. Cette classe contiendra plusieurs méthodes qui auront chacune leur rôle. Afin de mieux comprendre pourquoi telle ou telle méthode existe, lisez le tout une première fois ;

2. Certaines méthodes dépendent des autres.

Toutefois, certains points de ce tutoriel peuvent ne pas être lus au premier passage. Par exemple, le point 3.4 Méthode loopVar() n'est pas utile au premier passage, car pour bien le comprendre, il faut avoir compris les points 3.3 Méthode simpleVar(), 3.5 Méthode constantReplace() (la première partie de la fonction ligne 1 à ligne 15) et 3.6 Méthode parse().

I-C - Qu'est-ce qu'un système de templates ?

Un système de templates est un système permettant de séparer le code source d'une page (PHP) de son design (HTML).

Ce système présente quelques avantages :

1. Pouvoir travailler sur le design sans modifier une ligne de code ;

2. Pouvoir modifier le code sans se soucier du design ;

3. Créer facilement plusieurs design pour le site ;

4. Faciliter la maintenance du code ;

5. Permet de gérer un cache.

II - Exemple : test.php et test.tpl

Voici les fichiers d'exemples qui nous serviront de base pour les explications.

II-A - Simples variables

Exemple 2.1 : test.php - source avec des variables simples (placées hors boucle)

<?php
include 'template.php';

// Instanciation de la classe
$t = new Template('test.tpl');

// Simple variable
$t->simpleVar(array(
'WELCOME_MSG' => 'Bonjour !!',
'GOODBYE' => 'Au revoir !!',
));
$t->parse();
?>

Exemple 2.2 : test.tpl - template avec des variables simples (placées hors boucle)

<body>
{WELCOME_MSG} <br />
{GOODBYE}
</body>

II-B - Variables dans une boucle

Exemple 2.3 : test.php - source avec des variables placées dans une boucle

<?php
include 'template.php';

// Instanciation de la classe
$t = new Template('test.tpl');

// Variable avec boucle
$country_array = array('BE' => 'Belgique',
'FR' => 'France',
'ITA' => 'Italie',
);

foreach ($country_array as $id => $country) {
$t->loopVar('country', array(
'ID' => $id,
'COUNTRY' => $country,
));
}
$t->parse();
?>

Exemple 2.4 : test.tpl - template avec des variables placées dans une boucle

<body>
<!-- BEGIN country -->
boucle :
<b>{country.ID}</b> => {country.COUNTRY} <br/>
<!-- END country -->
</body>

III - La classe Template

Je vais vous expliquer pas à pas la réalisation d'une classe qui gère un système de templates.

III-A - Sa structure

Ne vous attardez pas trop sur cette structure, c'est juste pour vous donner un aperçu de la classe avec ses différentes méthodes.

Schéma - structure de la classe

III-B - Ses méthodes

Méthode __construct()

Cette méthode a pour but de :

- tester si le fichier .tpl joint au code PHP existe ;

- stocker le code HTML de la page test.tpl dans la variable $this->page :

méthode __construct()

function __construct($file) {
// Teste si le fichier existe et si il est autorisé en lecture
if(empty($file) or !file_exists($file) or !is_readable($file)) {
// Si le fichier est inexistant pas : erreur
die('Template error : file '.$file.' not found.');
} else {
// Ouverture du fichier
$handle = fopen($file, 'rb');

// Enregistrement du fichier dans $this->page
$this->page = fread($handle, filesize ($file));

// Fermeture du fichier
fclose ($handle);
}
}

Méthode simpleVar()

Cette méthode a pour but de :

- récupérer les constantes/données du fichier test.php (exemple 2.1) des simples variables ;

- stocker les constantes/données dans le tableau $this->infoTpl selon le schéma défini précédemment pour ce tableau :

méthode simpleVar()

function simpleVar($varArray = array()) {
// Si le tableau est vide, on stoppe le script
if (empty($varArray)) exit;

// Parcours du tableau
foreach ($varArray as $var => $data) {
// Enregistrement dans le tableau $this->infoTpl
$this->infoTpl['.'][][$var] = $data;
}
}

Méthode loopVar()

Cette méthode a pour but de :

- récupérer les constantes/données du fichier test.php (exemple 2.3) avec des variables placées dans une boucle ;

- stocker les constantes/données dans le tableau $this->infoTpl selon le schéma précédemment défini pour ce tableau.

méthode loopVar()

function loopVar($type, $varArray = array()) {
// Si le tableau est vide, on stoppe le script
if (empty($varArray)) exit;

// Calcule le nombre de lignes dans le type courant
// Si 0 ligne    => 0
// Si X lignes    => X
// Pourquoi X et non (X + 1) ?
// ->, Car on compte à partir de 0 donc X retourne toujours
//      le dernier id + 1
$lastID = (count($this->infoTpl[$type]) != 0) ? (count($this->infoTpl[$type])) : 0;

foreach ($varArray as $constant => $data) {
$this->infoTpl[$type][$lastID][$constant] = $data;
}
}

Méthode constantReplace()

Cette méthode a pour but de :

- remplacer '{CONSTANTE}' du fichier .tpl par sa donnée contenue dans le fichier test.php :

méthode constantReplace()

function constantReplace() {
// Parcours de tout le taleau $this->infoTpl
foreach($this->infoTpl as $type => $info) {
// Si le type est '.' càd
// provient de la fonction simpleVar()
// ou encore de constantes placées hors-boucle
if ($type == '.') {
for ($i = 0, $imax = count($info); $i < $imax; $i++) {
foreach ($info[$i] as $constant => $data) {
// Remplace {CONSTANTE} par les données correspondantes
// et met à  jour le code HTML du fichier test.tpl
// stocke dans $this->page
$data = (file_exists($data)) ? $this->includeFile($data) : $data;
$this->page = preg_replace('`{'.$constant.'}`', $data, $this->page);
}
}
// Sinon si le type est autre càd
// provient de la fonction loopVar()
// ou encore de constantes placées dans une boucle
} else {
// Calcule la taille du tableau $info
$infoSize = count($info);

// Variable qui contiendra le code à la place de
//  <!-- BEGIN country -->
//  {country.ID} => {country.COUNTRY}
// <!-- END country -->
$block = '';

// Parcourt le tableau $info
for ($i = 0; $i < $infoSize; $i++) {
// Encode les caractères spéciaux
$page = htmlentities($this->page);

// $page est une variable string
// Remplit le tableau $infoArray ligne par ligne
$infoArray = explode("\n", $page);

// Suppression des espaces blancs avant/après
// dus aux indentations du code
for ($k = 0, $kmax = count($infoArray); $k < $kmax; $k++) {
$infoArray[$k] = trim($infoArray[$k]);
}

// Récupération et formatage des tags
$startTag = '<!-- BEGIN '.$type.' -->';
$startTag = htmlentities($startTag);

$endTag = '<!-- END '.$type.' -->';
$endTag = htmlentities($endTag);

// Récupération de la clé des tags dans le tableau $infoArray
$startTag = (array_search($startTag, $infoArray)) + 1;
$endTag = (array_search($endTag, $infoArray)) - 1;
// Nombre de lignes entre les tags
$lengthTag = ($endTag - $startTag) + 1;

// Récupération de la portion du tableau
// délimitée par les tags (tags non compris)
$blockTag = array_slice($infoArray, $startTag, $lengthTag);

// Remise en type 'string' et non plus 'array'
// Facilitera le remplacement des constantes par leurs données
$blockTag = implode("\n", $blockTag);

// Remplacement des constantes par leurs données
foreach($info[$i] as $constant => $data) {
$data = (file_exists($data)) ? $this->includeFile($data) : $data;;
$blockTag = preg_replace('`{'.$type.'.'.$constant.'}`', $data, $blockTag);
}

// Ajout des données à la variable block globale pour la boucle
// Ajoute \n ou pas et ajoute les données
// de la nouvelle boucle à la suite de $block
$block = ($block == '') ? $blockTag : $block."\n".$blockTag;
}

// Mise en tableau de $block
// Facilitera l'opération de reconstitution des tableaux
$block = explode ("\n", $block);

// Coupe du tableau en 2
// $fisrtPart = début du tableau   ----->    <!-- BEGIN country -->
// $secondPart = <!-- BEGIN country -->    ----->    fin du tableau
$firstPart = array_slice($infoArray, 0, $startTag - 1);
$secondPart = array_slice($infoArray, $startTag + $lengthTag + 1);

// Reconstitution du code source
// en insérant au milieu les données
$page = array_merge($firstPart, $block, $secondPart);

// Décode les balises HTML qui étaient encodées avec htmlentities()
for ($i = 0, $imax = count($page); $i < $imax; $i++) {
$page[$i] = html_entity_decode($page[$i]);
}

// Met à jour le code HTML du fichier test.tpl
// stocke dans $this->page
$this->page = implode("\n", $page);
}
}
}

Méthode parse()

méthode parse()

function parse() {
$this->constantReplace();

echo $this->page;
}

IV - Méthode(s) supplémentaire(s)

Je vous présente ci-dessous quelques méthodes supplémentaires qui sont utiles dans certains cas (il n'y en a qu'une pour le moment).

IV-A - Méthode includeFile()

Cette méthode a pour but de :

- gérer l'inclusion de fichier :

méthode includeFile()

function includeFile ($file) {
// Enclenche la temporisation de sortie
ob_start();

include $file;

// Enregistre le contenu du tampon de sortie
$buffer = ob_get_contents();

// Efface le contenu du tampon de sortie
ob_clean();

// Retourne les données enregistrées
return $buffer;
}

Toutefois, pour que cette méthode prenne effet, il vous faut ajouter deux fois la même ligne à la méthode constantReplace().

Code à remplacer

// Remplace {CONSTANTE} par les données correspondantes
// et met à jour le code HTML du fichier test.tpl
// stocké dans $this->page

// Si $data est un fichier
$data = (file_exists($data)) ? $this->includeFile($data) : $data;
$this->page = preg_replace('`{'.$constant.'}`', $data, $this->page);

// Un peu plus loin dans le code

// Remplacement des constantes par leurs données
foreach($info[$i] as $constant => $data) {
$data = (file_exists($data)) ? $this->includeFile($data) : $data;
$blockTag = preg_replace('`{'.$type.'.'.$constant.'}`', $data, $blockTag);
}

V - Conclusion

V-A - Épilogue

Un système de templates simple n'est pas très difficile à faire. Il suffit de bien comprendre comment est fait le tableau $infoTpl.

V-B - Liens

Site : Comparatif des systèmes de template pour PHP
Forum : Quel système de template utilisez-vous ?
Livre : Smarty - PHP Template Programming and Applications, critique par Pierre-Baptiste Naigeon

VI - Annexe

VI-A - Structure de la classe (code PHP)

template.php

<?php
class Template {
private $page;                // Code source HTML de la page - fichier.tpl
private $infoTpl = array();   // Tableau des constantes => données

/**
* Vérifie l'existence du fichier
*/
function __construct($file) {
}

/**
* Enregistre les constantes dans $infoTpl
* infoTpl[.][][constant] = data;
*/
function simpleVar($varArray = array()) {
}

/**
* Enregistre les constantes dans $infoTpl
* infoTpl[type][lastID][constant] = data;
*
* - type    = nom du bloc contenant la boucle
* - lastID    = ID du tableau où se trouve le script
*/
function loopVar($type, $varArray = array()) {
}

/**
* Remplace les constantes par leurs données
*/
function constantReplace() {
}

/**
* Retourne le code HTML parser
*/
function parse() {
}
}
?>

VI-B - Structure avancée de la classe

Schéma - structure avancée de la classe

Explication du schéma :

1. __construct() se lance automatiquement à l'instanciation de la classe ;

2. Teste si le fichier passé en argument existe et est ouvert en lecture ;

3. Mise du code source HTML du fichier template dans $page ;

4. Récupération des constantes/données de test.php ;

5. Traitement avec simpleVar() ou loopVar() selon les besoins ;

6. Mise des constantes/données traitées dans $infoTpl ;

7. Parse le code avec parse() ;

8. parse() fait appel à constantReplace() ;

9. constantReplace() remplace les constantes par leurs données.

VI-C - Structure du tableau $InfoTpl

Le tableau $infoTpl sera de cette forme : Note : le texte entre accolades représente le nom des variables que j'utilise dans mon code :

$infoTpl

{$infoTpl}
Array
(
{$type}
[.] => Array
(
{$info[$i]}
[0] => Array
(
{$constant    => $data}
[WELCOME_MSG] => Bonjour !!
)

[1] => Array
(
[GOODBYE] => Aurevoir !!
)

)

[country] => Array
(
[0] => Array
(
[ID] => BE
[COUNTRY] => Belgique
)

[1] => Array
(
[ID] => FR
[COUNTRY] => France
)

[2] => Array
(
[ID] => ITA
[COUNTRY] => Italie
)

)

)