Rozszerzanie Latte
Latte zostało zaprojektowane z myślą o rozszerzalności. Choć jego standardowy zestaw tagów, filtrów i funkcji pokrywa wiele zastosowań, często trzeba dodać własną logikę lub pomocniki. Ta strona daje przegląd tego, jak rozszerzyć Latte, aby idealnie pasowało do wymagań Twojego projektu, od prostych pomocników po złożoną nową składnię.
Sposoby rozszerzania Latte
Oto szybki przegląd głównych sposobów dostosowywania i rozszerzania Latte:
- Własne filtry: do formatowania lub przekształcania danych
bezpośrednio w wyniku szablonu (np.
{$var|myFilter}). Idealne do zadań takich jak formatowanie dat, operacje na tekście czy zastosowanie konkretnego escapowania. Możesz ich użyć również do modyfikowania większych bloków treści HTML, opakowując treść w anonimowy{block}i stosując własny filtr. - Własne funkcje: do dodawania logiki wielokrotnego użytku,
którą można wywoływać w wyrażeniach szablonu (np.
{myFunction($arg1, $arg2)}). Przydatne do obliczeń, sięgania po pomocniki aplikacji albo generowania niewielkich fragmentów treści. - Własne tagi: do tworzenia zupełnie nowych konstrukcji
językowych (
{mytag}...{/mytag}albon:mytag). Tagi dają największe możliwości: pozwalają definiować własne struktury, sterować parsowaniem szablonu i implementować złożoną logikę renderowania. - Compiler passy: funkcje modyfikujące drzewo składniowe (AST) szablonu po parsowaniu, ale przed wygenerowaniem kodu PHP. Używane do zaawansowanych optymalizacji, kontroli bezpieczeństwa (jak Sandbox) czy automatycznych modyfikacji kodu.
- Własne loadery: do zmiany sposobu, w jaki Latte znajduje i wczytuje pliki szablonów (np. wczytywanie z bazy danych, z zaszyfrowanego magazynu itd.).
Wybór właściwego sposobu rozszerzenia jest kluczowy. Zanim stworzysz złożony tag, zastanów się, czy nie wystarczy prostszy filtr albo funkcja. Zilustrujmy to przykładem: implementacją generatora Lorem ipsum, który jako argument przyjmuje liczbę słów do wygenerowania.
- Jako tag?
{lipsum 40}– możliwe, ale tagi lepiej nadają się do struktur sterujących albo generowania złożonego markupu. Tagów nie da się użyć bezpośrednio w wyrażeniach. - Jako filtr?
{=40|lipsum}– technicznie działa, ale filtry mają przekształcać wejście. Tutaj40to argument, a nie przekształcana wartość. Semantycznie to nie brzmi dobrze. - Jako funkcja?
{lipsum(40)}– to najbardziej naturalne rozwiązanie! Funkcje przyjmują argumenty i zwracają wartości, więc doskonale nadają się do użycia w dowolnym wyrażeniu:{var $text = lipsum(40)}.
Ogólna wskazówka: funkcji używaj do obliczeń i generowania, filtrów do przekształcania, a tagów do nowych struktur językowych albo złożonego markupu. Passów używaj do manipulacji AST, a loaderów do pobierania szablonów.
Bezpośrednia rejestracja
Do pomocników specyficznych dla projektu albo szybkich dodatków Latte pozwala rejestrować filtry i funkcje bezpośrednio na
obiekcie Latte\Engine.
Do zarejestrowania filtra użyj addFilter(). Pierwszym argumentem funkcji filtra będzie wartość przed potokiem
|, a kolejnymi te podane po dwukropku :.
$latte = new Latte\Engine;
// definicja filtra (callable: funkcja, metoda statyczna itd.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);
// rejestracja
$latte->addFilter('truncate', $myTruncate);
// użycie w szablonie: {$text|truncate} albo {$text|truncate:100}
Do zarejestrowania funkcji używalnej w wyrażeniach szablonu użyj addFunction().
$latte = new Latte\Engine;
// definicja funkcji
$isWeekend = fn(DateTimeInterface $date) => $date->format('N') >= 6;
// rejestracja
$latte->addFunction('isWeekend', $isWeekend);
// użycie w szablonie: {if isWeekend($myDate)}Weekend!{/if}
Więcej szczegółów znajdziesz w Tworzenie własnych filtrów i Funkcje.
Robustny sposób: Latte Extension
Bezpośrednia rejestracja jest prosta, ale standardowym i zalecanym sposobem grupowania i dystrybucji rozszerzeń Latte są klasy rozszerzeń. Rozszerzenie działa jak centralny punkt konfiguracji do rejestrowania wielu tagów, filtrów, funkcji, compiler passów i innych rzeczy.
Po co używać rozszerzeń?
- Organizacja: trzyma powiązane rozszerzenia (tagi, filtry itd. dla konkretnej funkcjonalności) razem w jednej klasie.
- Ponowne użycie i udostępnianie: łatwo spakujesz swoje rozszerzenia do użytku w innych projektach albo do udostępnienia społeczności (np. przez Composera).
- Pełne możliwości: własne tagi i compiler passy można zarejestrować wyłącznie przez rozszerzenia.
Rejestracja rozszerzenia
Rozszerzenie rejestruje się w Latte metodą addExtension() (albo przez plik konfiguracyjny):
$latte = new Latte\Engine;
$latte->addExtension(new MyProjectExtension);
Jeśli zarejestrujesz kilka rozszerzeń, a definiują one tagi, filtry albo funkcje o tych samych nazwach, wygrywa rozszerzenie dodane jako ostatnie. Wynika z tego również, że Twoje rozszerzenia mogą nadpisywać natywne tagi, filtry i funkcje.
Za każdym razem, gdy zmienisz klasę, a automatyczne odświeżanie nie jest wyłączone, Latte samo przekompiluje Twoje szablony.
Tworzenie rozszerzenia
Aby utworzyć własne rozszerzenie, musisz stworzyć klasę dziedziczącą po Latte\Extension. Aby wyrobić sobie pogląd, jak takie rozszerzenie wygląda, zajrzyj do wbudowanego CoreExtension.
Zobaczmy, jakie metody możesz zaimplementować:
beforeCompile (Latte\Engine $engine): void
Wywoływana przed skompilowaniem szablonu. Metody można użyć na przykład do inicjalizacji związanych z kompilacją.
getTags(): array
Wywoływana przy kompilacji szablonu. Zwraca tablicę asocjacyjną nazwa tagu ⇒ callable, czyli funkcje parsujące tagi. Dowiedz się więcej.
public function getTags(): array
{
return [
'foo' => FooNode::create(...),
'bar' => BarNode::create(...),
'n:baz' => NBazNode::create(...),
// ...
];
}
Tag n:baz reprezentuje czysty n:atrybut, czyli tag,
który można zapisać wyłącznie jako atrybut.
W przypadku tagów foo i bar Latte automatycznie rozpozna, czy są parzyste, a jeśli tak, można je
automatycznie zapisywać za pomocą n:atrybutów, łącznie z wariantami z prefiksami n:inner-foo i
n:tag-foo.
Kolejność wykonywania takich n:atrybutów wynika z ich kolejności w tablicy zwracanej przez getTags(). Dlatego
n:foo jest zawsze wykonywany przed n:bar, nawet jeśli atrybuty są podane w tagu HTML w odwrotnej
kolejności jako <div n:bar="..." n:foo="...">.
Jeśli potrzebujesz ustalić kolejność n:atrybutów pochodzących z różnych rozszerzeń, użyj metody pomocniczej
order(), w której parametr before i/lub after określa, które tagi mają być
uporządkowane przed danym tagiem lub po nim.
public function getTags(): array
{
return [
'foo' => self::order(FooNode::create(...), before: 'bar'),
'bar' => self::order(BarNode::create(...), after: ['block', 'snippet']),
];
}
getPasses(): array
Wywoływana przy kompilacji szablonu. Zwraca tablicę asocjacyjną nazwa passu ⇒ callable, czyli funkcje reprezentujące tzw. compiler passy, które przechodzą przez AST i je modyfikują.
Także tutaj można użyć metody pomocniczej order(). Wartością parametrów before albo
after może być * w znaczeniu przed wszystkimi / po wszystkich.
public function getPasses(): array
{
return [
'optimize' => Passes::optimizePass(...),
'sandbox' => self::order($this->sandboxPass(...), before: '*'),
// ...
];
}
beforeRender (Latte\Runtime\Template $template): void
Wywoływana przed każdym renderowaniem szablonu. Metody można użyć na przykład do zainicjowania zmiennych używanych podczas renderowania.
afterRender (Latte\Runtime\Template $template): void
Wywoływana po każdym renderowaniu szablonu. Uruchamia się nawet wtedy, gdy renderowanie zakończy się wcześniej przez
{exitIf} albo zostanie przerwane wyjątkiem, więc to właściwe miejsce na sprzątanie lub pomiary.
getFilters(): array
Wywoływana przy rejestracji rozszerzenia metodą addExtension(). Zwraca filtry jako tablicę asocjacyjną
nazwa filtra ⇒ callable. Dowiedz się więcej.
public function getFilters(): array
{
return [
'batch' => $this->batchFilter(...),
'trim' => $this->trimFilter(...),
// ...
];
}
getFunctions(): array
Wywoływana przy rejestracji rozszerzenia metodą addExtension(). Zwraca funkcje jako tablicę asocjacyjną
nazwa funkcji ⇒ callable. Dowiedz się więcej.
public function getFunctions(): array
{
return [
'clamp' => $this->clampFunction(...),
'divisibleBy' => $this->divisibleByFunction(...),
// ...
];
}
getProviders(): array
Wywoływana przy rejestracji rozszerzenia metodą addExtension(). Zwraca tablicę providerów, czyli zwykle
obiektów, których tagi używają w czasie działania. Sięga się po nie przez $this->global->.... Dowiedz się więcej.
public function getProviders(): array
{
return [
'myFoo' => $this->foo,
'myBar' => $this->bar,
// ...
];
}
getCacheKey (Latte\Engine $engine): mixed
Wywoływana przed wyrenderowaniem szablonu. Wartość zwracana staje się częścią klucza, którego hash zawiera się w nazwie pliku skompilowanego szablonu. Dla różnych wartości zwracanych Latte wygeneruje więc różne pliki cache.