PHP alapú sablonmegoldás
Komoly alkalmazások fejlesztése elképzelhetetlen sablonok használata nélkül. Számtalan megoldás létezik, az egyik legismertebb talán a Smarty. Én most egy sokkal egyszerűbb, PHP alapú megoldást szeretnék bemutatni, amivel könnyű elindulni a sablonok használatának rögös útján, és ha megismeri az ember, lehet, hogy nem is érzi szükségét a továbblépésnek.
A megoldás nem tőlem származik, egyetemi éveim alatt találtam sablonozó rendszer után kutatva, a Weblaboron körülnézve is megtalálható Nagy Gusztáv webes jegyzetének végén. Ő Massasit jelöli meg forrásként, én annakidején máshol bukkantam rá (amit most természetesen nem sikerült megtalálnom).
Miért is használunk sablonokat? A választ már mindenki kívülről fújja: hogy különválasszuk a logikát a megjelenéstől (MVC). A program egy része előállítja a megjelenítéshez szükséges adatokat, majd átadja egy másik programrésznek (ez volna a sablon), amely a megfelelő formában megjeleníti azokat. Ezzel elkerülhető, hogy az adatbázisból kiolvasó ciklusunk teli legyen kiiratás parancsokkal, könnyebben megtaláljuk a hibákat mindkét oldalon.
A legtöbb sablonrendszer (páldául a Smarty is) saját szintaktikát alkalmaz a sablonokban. Ez a megoldás itt is a PHP nyelvet használja, hiszen az teljes körűen alkalmas erre. Ez magában rejti a veszélyt, hogy bármilyen más, ide nem tartozó kódot is írhatunk a sablonunkba. Ennek elkerülése a sablon szerzőjének (aki általában maga a programozó) felelőssége. Nem mindenki dolgozik hatalmas projekteken, ahol különválnak a folyamatok.
Jöjjön hát az osztály bemutatása, először egy egyszerű példán kereztül.
require 'Template.php';
// A tömb akár adatbázisból is jöhetne
$videos = array(
array(
'artist' => 'Al Di Meola',
'url' => 'http://youtube.com/watch?v=Q1F4etHibVo',
),
array(
'artist' => 'John Mclaughlin',
'url' => 'http://youtube.com/watch?v=pSybXLofaDM',
),
array(
'artist' => 'Paco De Lucia',
'url' => 'http://youtube.com/watch?v=2oyhlad64-s',
),
);
// A sablon osztály példányosítása
$template = new Template('index.tpl');
// A tömb elérhetővé tétele a sablon számára
$template->set('videos', $videos);
// A kimenet legenerálása, kiíratása
echo $template->fetch();
<h1>Híres, tehetséges gitárbűvészek</h1>
<div class="videos">
<? foreach($videos as $video): ?>
<div class="video">
<h2><?= $video['artist'] ?></h2>
<object
type="application/x-shockwave-flash"
data="<?= $video['url'] ?>"
width="480"
height="385"
>
<param name="movie" value="<?= $video['url'] ?>" />
</object>
</div>
<? endforeach ?>
</div>
Mit is csináltunk mindezzel? Egy tömböt, mely tartalmazta három gitárművész nevét és egy hivatkozást egy róla szóló Youtube videóra, átadtunk egy sablonfájlnak, ahol ezeket az előadókat videóikkal együtt megjelenítettük. Az első állomány nem szorul magyarázatra, az index.tpl
-ben lehetnek érdekes elemek, de ezek mind a PHP nyelv részei.
A <?=
egy <?php echo
kifejezés rövidítése. Van, aki ezt nem szereti, az ne használja, én vállalom a hátrányait.
Vezérlési szerkezetek esetében használható a <? foreach ($collection as $item): ?>
és <? endforeach ?>
páros és megfelelői a feltételvizsgálatra, ciklusokra is.
Nézzük hát a mágikus Template
osztály forrását.
class Template
{
private $variables; // A sablon változóit tárolja
private $file; // A sablon elérési útja
function __construct($file = null)
{
$this->setFile($file);
}
function setFile($file)
{
$this->file = $file;
}
// Változó hozzáadása
function set($name, &$value)
{
$this->variables[$name] = get_class($value) == 'Template' ? $value->fetch() : $value;
}
// Megnyitja a sablont, lefuttatja és visszaadja a kimenetét
function fetch($file = null) {
if ($file) {
$this->setFile($file);
}
// A változók elérhetővé tétele a helyi hatókörben
if (!empty($this->variables)) {
extract($this->variables);
}
// Kimeneti puffer indítása
ob_start();
// Ha létezik a fájl, beillesztjük
if (file_exists($this->file)) {
include $this->file;
} else {
// Hibakezelés
}
// A puffer tartalmának mentése
$contents = ob_get_contents();
// Majd ürítése
ob_end_clean();
return $content;
}
}
Haladjunk lépésről lépésre, és vizsgáljuk meg jobban osztályunkat.
function __construct($file = null)
{
$this->setFile($file);
}
A konstruktorban meghatározhatjuk, hogy milyen sablon fájlt használjunk. Ha ezt itt nem tesszük meg, megadhatjuk a fetch()
hívásakor is.
function set($name, &$value)
{
$this->variables[$name] = get_class($value) == 'Template' ? $value->fetch() : $value;
}
A set()
függvény segítségével változókat rendelhetünk hozzá a sablonhoz, amiket majd a kimenet generálása során használhat. A hozzárendelt változók $name argumentumban megadott néven érhetők majd el a sablonfájlban. Más sablon is hozzáadható, ezesetben a hozzáadandó változónak automatikusan meghívjuk a kimenetet generáló függvényét, és a kimenet lesz elérhető a változóban. A sablonok tehát egymásba ilymódon ágyazhatók.
function fetch($file = null) {
if ($file) {
$this->setFile($file);
}
// A változók elérhetővé tétele a helyi hatókörben
if (!empty($this->variables)) {
extract($this->variables);
}
// Kimeneti puffer indítása
ob_start();
// Ha létezik a fájl, beillesztjük
if (file_exists($this->file)) {
include $this->file;
} else {
// Hibakezelés
}
// A puffer tartalmának mentése
$contents = ob_get_contents();
// Majd ürítése
ob_end_clean();
return $content;
}
A fetch()
függvény generálja le a kimenetet, amit egy karakterláncban ad vissza. Ezt bátran felhasználhatjuk bármire.
Mit nyerhetünk mindezzel? Egészen programunk végéig nem kell, hogy bármit is küldjünk a kimenetre, bármikor hívhatunk header()
függvényeket, a kimeneten bármilyen módosítást végrehajthatunk, akár programunk legvégén is, és legfőképpen szebb, átláthatóbb kóddal fogunk dolgozni.
Remélem sikerült ezen egyszerű példán át megismertetnem az olvasókkal a sablonkezelés alapjait, akik addig nem használtak ilyet, ezek után remélhetőleg bátrabban vágnak bele.
■
Majdnem ugyanezt ...
Annyival több az enyém, hogy van display() és generate() metódus (előbbi echo, utóbbi return), helper támogatás és egy autoClean flag, amivel a render utáni automatikus változóhozzárendelés nullázást lehet szabályozni.
A működése nálam annyiban más, hogy egy TemplateEngine-t kell példányosítani és a display(), generate() kapja paraméterként a template fájl nevét. De ki fogom egészíteni a te megoldásoddal is, kinek mi a szimpatikusabb. Jelenleg van nekem egy clearAssign() és egy clearAllAssign() metódusom (előbbi egy vagy több, utóbbi az összes változóhozzárendelést törli -- autoClean az utóbbit hívja meg).
kis optimalizáció
smarty
Objektumok
Én belenéztem egyszer a
Egyébként én még mindig az xslt mellett vagyok sablonozásnál, ki lehet tenni kliens oldalra, de ha muszáj szerver oldalon gányolni vele, akkor szerintem nem annyira bonyolult egy egyszerűbb xslt->php konvertálót írni.
Azért
Mégpedig?
Csekkit
Illetve meg lehet fűszerezni ezt az egészet még olyannal is, hogy ha a behelyettesítendő _nf-re végződik {szamertek_nf}, akkor ráküldeni egy number_format függvényt is, vagy bármi más.
Vagy, ha az átadott $r tömbben nem található valamilyen változó, akkor egy globális sablontömbből vegyen értéket, tipikusan pl. a site url-je {site_url}, és akkor azt nem kell minden sablonnál átadni.
Kérdés
Egyelőre nem tűnik szívásnak,
Szerintem ez:
jobb vagy rosszabb
Számomra az a legnagyobb ellenérv a saját formátumot használó sablonozó rendszerekkel szemben, hogy ezzel plusz egy hiba hordozót építünk be az alkalmazásunkba. Egy rosszul megírt sablonozóból fakadó teljesítmény problémákról már nem is beszélve.
Semmi értelme szerintem az
Egyetértek
phpe[TAB] --> <?php echo ${msg} ?>${cursor}
phpt[TAB] --> <?php echo __('${msg}'${params}) ?>${cursor}
phpif[TAB] --> ...
hihi jó ez az n+1-edik kell-e
Na de nem is szólnék hozzá túl bőven, 1 gondolat: a forráskódban a
get_class($value) == 'Template'
helyett én inkább azt írnám, hogy$value instanceof Template
, mert hogy az akkor is működni fog ha származtatunk aTemplate
osztályból.érdemes megnézni ezt is
http://github.com/kohana/core/blob/master/classes/kohana/view.php
Anno még néztem olyan
Valamelyik nagyobb framework-höz volt, ahogy nézem kohana is ilyesmit tol, de én úgy emlékszem másik fw-hez volt először.