Principes et mise en œuvre d'un moteur de templates en PHP

La séparation du code PHP et du HTML statique améliore considérablement la lisibilité et la maintanabilité des applications web. Un moteur de templates facilite cette séparation en permettant de générer des pages HTML dynamiques à partir de fichierss modèles.

Avantages des moteurs de templates

  • Séparation des responsabilités : Permet aux développeurs de se concentrer sur la logique et aux designers sur la présentation.
  • Performance : Les templates compilés et mis en cache réduisent la charge du serveur.
  • Sécurité : Restreint les opérations dangereuses dans les templates, évitant les erreurs de conception.
  • Maintenabilité : Simplifie la mise à jour et la modification du code.

Structure de fichiers pour un moteur de templates

Pour mettre en place un système de templates, organisez votre projet comme suit :

  • index.php : Fichier principal contenant la logique métier.
  • template_engine.php : Classe d'initialisation du moteur de templates.
  • views/ : Répertoire pour les fichiers de templates (.tpl).
  • compiled/ : Répertoire pour les fichiers compilés.
  • cache/ : Répertoire pour les fichiers mis en cache.
  • config/ : Répertoire pour les fichiers de configuration.

Implémentation du moteur de templates

Fichier principle : index.php

<?php
header('Content-Type: text/html; charset=UTF-8');
define('BASE_PATH', dirname(__FILE__));
define('TEMPLATE_DIR', BASE_PATH . '/views/');
define('COMPILED_DIR', BASE_PATH . '/compiled/');
define('CACHE_DIR', BASE_PATH . '/cache/');
define('ENABLE_CACHE', true);

if (ENABLE_CACHE) {
    ob_start();
}

require_once BASE_PATH . '/includes/TemplateEngine.php';

$userName = 'Alice';
$dataList = [10, 20, 30, 40, 50];
$isActive = true;

$engine = new TemplateEngine();
$engine->setVariable('username', $userName);
$engine->setVariable('is_active', $isActive);
$engine->setVariable('items', $dataList);
$engine->render('homepage.tpl');
?>

Fichier de template : homepage.tpl


<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Page d'accueil</title>
</head>
<body>
    {include "header.php"}
    
    <h1>Bienvenue, {$username} !</h1>
    
    {if $is_active}
        <p>Le compte est actif.</p>
    {else}
        <p>Le compte est inactif.</p>
    {/if}
    
    <ul>
    {foreach $items(index, value)}
        <li>Élément {@index}: {@value}</li>
    {/foreach}
    </ul>
    
    {#}Ce commentaire ne sera pas affiché dans la page finale.{#}
</body>
</html>

Classe de moteur de templates : TemplateEngine.php

<?php
class TemplateEngine {
    private $variables = [];
    private $templateContent = '';

    public function setVariable($key, $value) {
        $this->variables[$key] = $value;
    }

    public function render($templateFile) {
        $templatePath = TEMPLATE_DIR . $templateFile;
        if (!file_exists($templatePath)) {
            die('Erreur : Fichier de template introuvable.');
        }
        $this->templateContent = file_get_contents($templatePath);
        $this->processTemplate();
        $compiledFile = COMPILED_DIR . md5($templateFile) . '.php';
        file_put_contents($compiledFile, $this->templateContent);
        include $compiledFile;
    }

    private function processTemplate() {
        $this->parseVariables();
        $this->parseConditionals();
        $this->parseLoops();
        $this->parseIncludes();
        $this->removeComments();
    }

    private function parseVariables() {
        $pattern = '/\{\$([\w]+)\}/';
        $this->templateContent = preg_replace_callback($pattern, function($matches) {
            $varName = $matches[1];
            return isset($this->variables[$varName]) ? htmlspecialchars($this->variables[$varName]) : '';
        }, $this->templateContent);
    }

    private function parseConditionals() {
        $pattern = '/\{if\s+\$([\w]+)\}(.+?)\{\/if\}/s';
        $this->templateContent = preg_replace_callback($pattern, function($matches) {
            $varName = $matches[1];
            $content = $matches[2];
            if (isset($this->variables[$varName]) && $this->variables[$varName]) {
                $elsePattern = '/\{else\}(.+)$/s';
                if (preg_match($elsePattern, $content, $elseMatches)) {
                    return substr($content, 0, strpos($content, '{else}'));
                }
                return $content;
            } else {
                if (preg_match('/\{else\}(.+)$/s', $content, $elseMatches)) {
                    return $elseMatches[1];
                }
                return '';
            }
        }, $this->templateContent);
    }

    private function parseLoops() {
        $pattern = '/\{foreach\s+\$([\w]+)\((\w+),(\w+)\)\}(.+?)\{\/foreach\}/s';
        $this->templateContent = preg_replace_callback($pattern, function($matches) {
            $arrayName = $matches[1];
            $keyVar = $matches[2];
            $valueVar = $matches[3];
            $loopContent = $matches[4];
            $output = '';
            if (isset($this->variables[$arrayName]) && is_array($this->variables[$arrayName])) {
                foreach ($this->variables[$arrayName] as $key => $value) {
                    $tempContent = $loopContent;
                    $tempContent = str_replace("{@$keyVar}", $key, $tempContent);
                    $tempContent = str_replace("{@$valueVar}", $value, $tempContent);
                    $output .= $tempContent;
                }
            }
            return $output;
        }, $this->templateContent);
    }

    private function parseIncludes() {
        $pattern = '/\{include\s+"([^"]+)"\}/';
        $this->templateContent = preg_replace_callback($pattern, function($matches) {
            $file = $matches[1];
            $filePath = TEMPLATE_DIR . $file;
            if (file_exists($filePath)) {
                return file_get_contents($filePath);
            }
            return '';
        }, $this->templateContent);
    }

    private function removeComments() {
        $pattern = '/\{#\}.*?\{#\}/s';
        $this->templateContent = preg_replace($pattern, '', $this->templateContent);
    }
}
?>

Processus de génération des templates

Lorsqu'une requête est reçue, le moteur de templates effectue les étapes suivantes :

  1. L'instance de TemplateEngine initialise les variables à injecter via setVariable().
  2. La méthode render() charge le fichier de template et lance le traitement.
  3. Les expressions régulières remplacent les balises dynamiques par du code PHP ou du contenu statique.
  4. Le résultat est compilé et stocké dans le répertoire compiled/.
  5. Le fichier compilé est inclus pour générer la sortie HTML finale.
  6. Si la mise en cache est activée, le résultat est sauvegardé pour les requêtes suivantes, en vérifiant les dates de modification des fichiers.

Étiquettes: PHP moteur de templates Expressions Régulières compilation mise en cache

Publié le 3 juillet à 22h12