Extending Latte
A Latte nagyon rugalmas, és sokféleképpen bővíthető: hozzáadhatsz egyéni szűrőket, funkciókat, címkéket, betöltőket stb. Megmutatjuk, hogyan kell ezt megtenni.
Ez a fejezet a Latte bővítésének különböző módjait ismerteti. Ha a változtatásait különböző projektekben szeretné újra felhasználni, vagy ha meg akarja osztani másokkal, akkor ún. kiterjesztést kell létrehoznia.
Hány út vezet Rómába?
Mivel a Latte kiterjesztésének néhány módja keveredhet, először próbáljuk meg elmagyarázni a köztük lévő különbségeket. Példaként próbáljunk meg egy Lorem ipsum generátort implementálni, amelynek átadjuk a generálandó szavak számát.
A Latte nyelv fő konstrukciója a tag. Egy generátort úgy tudunk megvalósítani, hogy a Latte-t egy új címkével bővítjük:
{lipsum 40}
A tag nagyszerűen fog működni. A generátor tag formájában azonban nem biztos, hogy elég rugalmas, mert nem lehet kifejezésben használni. Egyébként a gyakorlatban ritkán van szükség címkék generálására; és ez jó hír, mert a címkék bonyolultabb módja a bővítésnek.
Oké, próbáljunk meg címke helyett szűrőt létrehozni:
{=40|lipsum}
Ismét egy érvényes lehetőség. De a szűrőnek az átadott értéket valami mássá kell átalakítania. Itt a
40
értéket, amely a generált szavak számát jelzi, használjuk a szűrő argumentumaként, nem pedig az
átalakítandó értékként.
Próbáljuk meg tehát a függvényt használni:
{lipsum(40)}
Ez az! Ehhez a konkrét példához a függvény létrehozása az ideális kiterjesztési pont. Bárhol meghívhatja, ahol például egy kifejezést elfogadnak:
{var $text = lipsum(40)}
Szűrők
Hozzon létre egy szűrőt a nevének és bármely PHP-meghívható elemnek, például függvénynek a regisztrálásával:
$latte = new Latte\Engine;
$latte->addFilter('shortify', fn(string $s) => mb_substr($s, 0, 10)); // a szöveget 10 karakterre rövidíti.
Ebben az esetben jobb lenne, ha a szűrő kapna egy további paramétert:
$latte->addFilter('shortify', fn(string $s, int $len = 10) => mb_substr($s, 0, $len));
A sablonban így használjuk:
<p>{$text|shortify}</p>
<p>{$text|shortify:100}</p>
Mint látható, a függvény a következő argumentumként a szűrő bal oldalát kapja a pipe |
as the first
argument and the arguments passed to the filter after :
előtt.
Természetesen a szűrőt reprezentáló függvény tetszőleges számú paramétert fogadhat el, és a változó paraméterek is támogatottak.
Ha a szűrő egy karakterláncot ad vissza HTML-ben, akkor megjelölheted, hogy a Latte ne automatikusan (és ezért duplán)
szedje ki. Így elkerülhető a |noescape
megadása a sablonban. A legegyszerűbb, ha a sztringet egy
Latte\Runtime\Html
objektumba csomagoljuk, a másik lehetőség a Contextual
Filters.
$latte->addFilter('money', fn(float $amount) => new Latte\Runtime\Html("<i>$amount EUR</i>"));
Ebben az esetben a szűrőnek biztosítania kell az adatok helyes kikerülését.
Az osztályt használó szűrők
A szűrő definiálásának második módja az osztály
használata. Létrehozunk egy metódust a TemplateFilter
attribútummal:
class TemplateParameters
{
public function __construct(
// paraméterek
) {}
#[Latte\Attributes\TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
return mb_substr($s, 0, $len);
}
}
$params = new TemplateParameters(/* ... */);
$latte->render('template.latte', $params);
Szűrő betöltő
Az egyedi szűrők regisztrálása helyett létrehozhat egy úgynevezett betöltőt, amely egy olyan függvény, amelyet a szűrő nevével mint argumentummal hívunk meg, és amely visszaadja a PHP hívhatóságát, vagy nullát.
$latte->addFilterLoader([new Filters, 'load']);
class Filters
{
public function load(string $filter): ?callable
{
if (in_array($filter, get_class_methods($this))) {
return [$this, $filter];
}
return null;
}
public function shortify($s, $len = 10)
{
return mb_substr($s, 0, $len);
}
// ...
}
Kontextuális szűrők
A kontextuális szűrő olyan szűrő, amely az első paraméterként egy objektumot fogad el: Latte\Runtime\FilterInfo, amelyet a klasszikus szűrőkhöz hasonlóan további paraméterek követnek. Ugyanúgy regisztrálódik, maga a Latte ismeri fel, hogy a szűrő kontextuális:
use Latte\Runtime\FilterInfo;
$latte->addFilter('foo', function (FilterInfo $info, string $str): string {
// ...
});
A kontextusszűrők képesek felismerni és megváltoztatni a $info->contentType
változóban kapott
tartalomtípust. Ha a szűrőt klasszikusan egy változó (pl. {$var|foo}
) felett hívjuk meg, a
$info->contentType
nullát fog tartalmazni.
A szűrőnek először azt kell ellenőriznie, hogy a bemeneti karakterlánc tartalomtípusa támogatott-e. Meg is változtathatja azt. Példa egy olyan szűrőre, amely szöveget (vagy nullot) fogad el és HTML-t ad vissza:
use Latte\Runtime\FilterInfo;
$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
// először ellenőrizzük, hogy a bemenet tartalma text-e.
if (!in_array($info->contentType, [null, ContentType::Text])) {
throw new Exception("Szűrő |pénz nem kompatibilis tartalomtípusban használt $info->contentType.");
}
// a tartalomtípust HTML-re módosítja
$info->contentType = ContentType::Html;
return "<i>$amount EUR</i>";
});
Ebben az esetben a szűrőnek biztosítania kell az adatok helyes escapingjét.
Minden olyan szűrő, amelyet blokkok felett használunk (pl. mint a
{block|foo}...{/block}
) kontextusfüggőnek kell lennie.
Funkciók
Alapértelmezés szerint minden natív PHP függvény használható a Latte-ban, hacsak a homokozó nem tiltja le. De definiálhatsz saját függvényeket is. Ezek felülírhatják a natív függvényeket.
Hozzon létre egy függvényt a nevének és bármely PHP hívhatóságának regisztrálásával:
$latte = new Latte\Engine;
$latte->addFunction('random', function (...$args) {
return $args[array_rand($args)];
});
A használat ezután ugyanaz, mint a PHP-funkció hívásakor:
{random(apple, orange, lemon)} // prints for example: apple
Az osztályt használó függvények
A függvények definiálásának második módja az osztály használata. Létrehozunk egy metódust a
TemplateFunction
attribútummal:
class TemplateParameters
{
public function __construct(
// paraméterek
) {}
#[Latte\Attributes\TemplateFunction]
public function random(...$args)
{
return $args[array_rand($args)];
}
}
$params = new TemplateParameters(/* ... */);
$latte->render('template.latte', $params);
Betöltők
A betöltők felelősek a sablonok betöltéséért egy forrásból, például egy fájlrendszerből. Ezek beállítása a
setLoader()
módszerrel történik:
$latte->setLoader(new MyLoader);
A beépített betöltők a következők:
FileLoader
Alapértelmezett betöltő. Sablonokat tölt be a fájlrendszerből.
A fájlokhoz való hozzáférés korlátozható az alapkönyvtár beállításával:
$latte->setLoader(new Latte\Loaders\FileLoader($templateDir));
$latte->render('test.latte');
StringLoader
Sablonokat tölt be stringekből. Ez a betöltő nagyon hasznos a unit teszteléshez. Kisebb projekteknél is használható, ahol érdemes az összes sablon egyetlen PHP fájlban tárolni.
$latte->setLoader(new Latte\Loaders\StringLoader([
'main.file' => '{include other.file}',
'other.file' => '{if true} {$var} {/if}',
]));
$latte->render('main.file');
Egyszerűsített használat:
$template = '{if true} {$var} {/if}';
$latte->setLoader(new Latte\Loaders\StringLoader);
$latte->render($template);
Egyéni betöltő létrehozása
A Loader egy osztály, amely a Latte\Loader interfészt valósítja meg.
Címkék
A templating motor egyik legérdekesebb funkciója, hogy új nyelvi konstrukciókat definiálhatunk címkék segítségével. Ez egyben egy összetettebb funkcionalitás is, és meg kell értenie, hogyan működik a Latte belsőleg.
A legtöbb esetben azonban nincs szükség a címkére:
- Ha valamilyen kimenetet kell generálnia, használjon helyette függvényt.
- ha valamilyen bemenetet kellene módosítania és visszaadnia, használjon helyette filtert
- ha egy szövegrészletet kellene szerkesztenie, akkor csomagolja be egy
{block}
címkével és egy szűrővel - ha nem kellett volna semmit kiadnia, hanem csak egy függvényt hívnia, hívja meg a
{do}
Ha mégis taget akarsz létrehozni, remek! Minden lényeges tudnivalót megtalálsz a Bővítmény létrehozása című fejezetben.
Fordítói passzusok
A fordítói átmenetek olyan függvények, amelyek módosítják az AST-eket vagy információt gyűjtenek bennük. A Latte-ban például egy homokozót így valósítanak meg: végigjárja egy AST összes csomópontját, megtalálja a függvény- és metódushívásokat, és azokat ellenőrzött hívásokkal helyettesíti.
A címkékhez hasonlóan ez is összetettebb funkcionalitás, és meg kell értenie, hogyan működik a Latte a motorháztető alatt. Az összes lényeges tudnivaló megtalálható a Bővítmény létrehozása fejezetben.