Stavo progettando un sistema per gestire i template quando sono arrivato a una soluzione abbastanza originale ma al tempo stesso semplice.
Non avendo letto molti articoli a proposito voglio discuterla insieme a voi.
Dunque...sono partito dai template classici...una pagina HTML con dei parametri racchiusi da parentesi {}.
Per chi non lo sapesse un template si presenta così
codice:
<html>
<head>
</head>
<body>
{testo}
</body>
</html>
Via php posso sostituire a {testo} quello che voglio 'riciclando' lo stesso codice html.
Però ci sono diversi problemi:
- se effettuo la sostituzione non posso più effettuarne una seconda
- se chiamo due parametri con lo stesso nome vengono sostituiti entrambi (pericolo di collisioni)
- in generale questa soluzione ha la limitazione di non permettere vere e proprie impaginazioni dinamiche!
Allora mi sono inventato una nuova notazione.
I parametri tra parentesi li chiamo con fantasia parametri.
A fianco dei parametri mi sono inventato una seconda entità che chiamo blocco.
La notazione dei parametri è quella solita: {parametro}
I blocchi sono invece indicati dalla seguente notazione
Da osservare che visto che i tag di apertura e chiusura sono dei commenti non generano alcun output e mantiene la compatibilità con il precedente metodo a parametri semplici.
Le due entità sono perfettamente complementari.
I parametri possono essere sostituiti mentre i blocchi permettono sia di limitare le sostituzioni al loro contenuto sia di ricopiare se stessi per sostituzioni iterative.
Penso che con qualche esempio risulti più comprensibile.
Prima di questo presento il codice.
E' un pò lunghetto ma l'ho commentato.
La funzione più interessante è comunque parse_block() che commento brevemente.
Se non vi interessa potete saltare subito al semplice esempio!
codice:
::parse_block($block, $params, $bremove = true);
- $block rappresenta il nome del blocco
- $params è un'array associativo : la chiave rappresenta il nome del
parametro mentre il valore associato rappresenta il testo
che vogliamo venga sosituito nelle parentesi {}
- $bremove è un bool : di default è true cioè il blocco (incluso il tag-commento)
scompare dal codice durante la sostituzione;
se viene settato a false il blocco viene ricopiato permettendo una nuova sostituzione
Da notare che se un parametro con lo stesso nome è in un altro blocco o fuori dal blocco specificato non viene modificato.
Se il blocco contiene un blocco nidificato questo viene ricopiato senza modifiche (sempre che non contenga un parametro con lo stesso nome di quello nel blocco esterno!)
Qui il codice della classe
codice:
// Gestisce i templates
//
// I templates sono fondamentalmente pagine html con possibilità di sostituire
// dinamicamente alcune entità.
// Le entità sono
// - parametri : {nome_parametro}
// - blocchi :
//
//
// ...
// blocco
// ...
//
//
// Tutto quello tra beg ed end fa parte del blocco
// Il blocco può contenere codice HTML, parametri o altri blocchi
// Essendo racchiuso in un commento html non genera output
//
// In entrambi i casi occorre che non ci siano spazi tra il tag di apertura
// e quello di chiusura( e {...} )
//
class zz_template
{
// carica template da file
function loadfile($templatefilename){
$content="";
if(file_exists($templatefilename)){
if($hfile = fopen($templatefilename, "r")){
$content = fread($hfile, filesize($templatefilename));
fclose($hfile);
}
}
$this->loadstring($content);
}
// carica template da stringa
function loadstring($template){
$this->template = $template;
}
// restituisce il codice prodotto dal template
function get(){
return($this->template);
}
// effettua la sostituzione di tutti i parametri (a prescindere dal blocco)
// sostituisce tutti i parametri con i valori
// i nomi dei parametri e i valori sono specificati rispettivamente dalle
// chiavi e dai valori dell'array $params
// Se bremove è false aggiunge una copia del parametro template
// dopo la sostituzione in modo da rendere possibili sostituzioni iterative
//
function parse($params, $bremove = true){
if($params){
$keys = array_keys($params);
for($i=0; $i<count($keys); $i++){
$param_name = $keys[$i];
$param_value = $params[$param_name];
$this->replace($param_name, $param_value, $bremove);
}
}
return($this->template);
}
// come parse ma effettua la sostituzione solo sul blocco block
// sono sostituiti tutti e solo i parametri che si trovano sia nel blocco che
// nelle chiavi di $params
// Se bremove è false viene effettuata una copia del template-blocco in modo
// da rendere possibili sostituzioni iterative
// Se ci sono parametri con lo stesso nome ma fuori dal blocco o in altri blocchi
// non vengono sostituiti
//
function parse_block($block, $params, $bremove = true){
$block_str = $this->getBlock($block);
$block_content = $this->getBlockContent($block);
$template = new zz_template();
$template->loadstring($block_content);
$template->parse($params, true);
$text = $template->get().($bremove?"":$block_str);
$this->template = str_replace($block_str, $text, $this->template);
}
// Rimuove il blocco template block
function remove_block($block){
$this->template = str_replace($this->getBlock($block), "", $this->template);
}
////////////////////////////////////////////////////////////
// metodi privati
function replace($param_name, $param_value, $bremove = true){
$param_tag = "{".$param_name."}";
$text = $param_value.($bremove?"":$param_tag);
$this->template = str_replace($param_tag, $text, $this->template);
}
// prende template (inclusi i tag di beg/end)
function getBlock($block){
$startblock_str = "";
$endblock_str = "";
if(($s = strpos($this->template, $startblock_str))===false) return "";
if(($e = strpos($this->template, $endblock_str)) ===false) return "";
return(substr($this->template, $s, $e-$s+strlen($endblock_str)));
}
// prende solo la parte interna al blocco
function getBlockContent($block){
$startblock_str = "";
$endblock_str = "";
if(($s = strpos($this->template, $startblock_str))===false) return "";
if(($e = strpos($this->template, $endblock_str)) ===false) return "";
$offset = strlen($startblock_str);
$s += $offset;
return(substr($this->template, $s, $e-$s));
}
// template e prodotto
var $template;
}
Ok finalmente un esempio semplice
Ho preparato questo template
codice:
<html>
<head>
</head>
<body>
<ul>
[*]{testo}
[/list]
</body>
</html>
che ho salvato nel file 'template.html'
Ora scrivo lo script php
codice:
// include il file con la classe (beh...chiamatelo pure come volete)
require_once("zz_template.php");
// creo una istanza della classe
$template = new zz_template();
// carico il template dal file html
$template->loadfile("template.html");
// semplice ciclo da 1 a 10
for($i=1;$i<=10;$i++){
// voglio che {testo} sia sostituito con l'indice i
$param["testo"] = $i;
// effettuo la sostituzione
$template->parse_block("blocco", $param, false);
}
// il blocco non serve più-> rimuoviamolo
$template->remove_block("blocco");
// stampiamo il risultato
echo $template->get();
Il codice sorgente sarà questo
codice:
<html>
<head>
</head>
<body>
<ul>[*]1 [*]2 [*]3 [*]4 [*]5 [*]6 [*]7 [*]8 [*]9 [*]10 [/list]
</body>
</html>
Vista la lunghezza della pillola non mi sembrava il caso di presentare un esempio più complesso...considerate comunque che ogni blocco potrebbe contenere frammenti HTML molto più complessi, più parametri,...
Addirittura potremmo avere blocchi nidificati i quali possono venire parsati in fasi diverse.
Piaciuta la pillola ?