Rozszerzanie Latte

Latte zostało zaprojektowane z myślą o rozszerzalności. Chociaż jego standardowy zestaw znaczników, filtrów i funkcji obejmuje wiele przypadków użycia, często trzeba dodać własną, specyficzną logikę lub narzędzia pomocnicze. Ta strona zawiera przegląd sposobów rozszerzenia 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:

  • Filtry niestandardowe: Do formatowania lub przekształcania danych bezpośrednio w wyjściu szablonu (np. {$var|myFilter}). Idealne do zadań takich jak formatowanie dat, modyfikacje tekstu lub stosowanie specyficznego escapowania. Można je również użyć do modyfikacji większych bloków zawartości HTML, opakowując zawartość w anonimowy {block} i stosując na nim własny filtr.
  • Funkcje niestandardowe: Do dodawania logiki wielokrotnego użytku, którą można wywoływać w wyrażeniach w szablonie (np. {myFunction($arg1, $arg2)}). Przydatne do obliczeń, dostępu do funkcji pomocniczych aplikacji lub generowania małych fragmentów treści.
  • Znaczniki niestandardowe: Do tworzenia zupełnie nowych konstrukcji językowych ({mytag}...{/mytag} lub n:mytag). Znaczniki oferują najwięcej możliwości, pozwalają definiować własne struktury, kontrolować parsowanie szablonu i implementować złożoną logikę renderowania.
  • Przejścia kompilatora: Funkcje, które modyfikują abstrakcyjne drzewo składni (AST) szablonu po parsowaniu, ale przed generowaniem kodu PHP. Używane do zaawansowanych optymalizacji, kontroli bezpieczeństwa (takich jak Sandbox) lub automatycznych modyfikacji kodu.
  • Niestandardowe loadery: Do zmiany sposobu, w jaki Latte wyszukuje i ładuje pliki szablonów (np. ładowanie z bazy danych, zaszyfrowanego magazynu itp.).

Wybór odpowiedniej metody rozszerzenia jest kluczowy. Zanim stworzysz złożony znacznik, zastanów się, czy nie wystarczyłby prostszy filtr lub funkcja. Pokażmy to na przykładzie: implementacja generatora Lorem ipsum, który jako argument przyjmuje liczbę słów do wygenerowania.

  • Jako znacznik? {lipsum 40} – Możliwe, ale znaczniki są bardziej odpowiednie dla struktur sterujących lub generowania złożonych znaczników. Znaczników nie można używać bezpośrednio w wyrażeniach.
  • Jako filtr? {=40|lipsum} – Technicznie to działa, ale filtry są przeznaczone do transformacji wartości wejściowej. Tutaj 40 jest argumentem, a nie wartością, która jest transformowana. To wydaje się semantycznie niepoprawne.
  • Jako funkcja? {lipsum(40)} – To jest najbardziej naturalne rozwiązanie! Funkcje przyjmują argumenty i zwracają wartości, co jest idealne do użycia w dowolnym wyrażeniu: {var $text = lipsum(40)}.

Ogólne zalecenie: Używaj funkcji do obliczeń/generowania, filtrów do transformacji i znaczników do nowych konstrukcji językowych lub złożonych znaczników. Przejść używaj do manipulacji AST, a loaderów do pobierania szablonów.

Bezpośrednia rejestracja

Dla narzędzi pomocniczych specyficznych dla projektu lub szybkich rozszerzeń, Latte umożliwia bezpośrednią rejestrację filtrów i funkcji w obiekcie Latte\Engine.

Do rejestracji filtra użyj metody addFilter(). Pierwszym argumentem Twojej funkcji filtrującej będzie wartość przed znakiem |, a kolejne argumenty to te, które są przekazywane za dwukropkiem :.

$latte = new Latte\Engine;

// Definicja filtra (obiekt wywoływalny: funkcja, metoda statyczna itp.)
$myTruncate = fn(string $s, int $length = 50) => mb_substr($s, 0, $length);

// Rejestracja
$latte->addFilter('truncate', $myTruncate);

// Użycie w szablonie: {$text|truncate} lub {$text|truncate:100}

Możesz również zarejestrować Filter Loader, funkcję, która dynamicznie dostarcza obiekty wywoływalne filtrów według żądanej nazwy:

$latte->addFilterLoader(fn(string $name) => /* zwraca obiekt wywoływalny lub null */);

Do rejestracji funkcji użytecznej 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 informacji znajdziesz w części Tworzenie niestandardowych filtrówFunkcji.

Solidny sposób: Rozszerzenie Latte

Chociaż bezpośrednia rejestracja jest prosta, standardowym i zalecanym sposobem pakowania i dystrybucji rozszerzeń Latte jest użycie klas Extension. Extension służy jako centralny punkt konfiguracyjny do rejestracji wielu znaczników, filtrów, funkcji, przejść kompilatora i innych elementów.

Dlaczego używać Extensions?

  • Organizacja: Utrzymuje powiązane rozszerzenia (znaczniki, filtry itp. dla konkretnej funkcji) razem w jednej klasie.
  • Wielokrotne użycie i udostępnianie: Łatwo spakujesz swoje rozszerzenia do użytku w innych projektach lub do udostępnienia społeczności (np. przez Composer).
  • Pełna moc: Niestandardowe znaczniki i przejścia kompilatora mogą być rejestrowane tylko za pośrednictwem Extensions.

Rejestracja Rozszerzenia

Extension rejestruje się w Latte za pomocą metody addExtension() (lub za pośrednictwem pliku konfiguracyjnego):

$latte = new Latte\Engine;
$latte->addExtension(new MyProjectExtension);

Jeśli zarejestrujesz wiele rozszerzeń i definiują one tak samo nazwane znaczniki, filtry lub funkcje, pierwszeństwo ma ostatnio dodane rozszerzenie. Oznacza to również, że Twoje rozszerzenia mogą nadpisywać natywne znaczniki/filtry/funkcje.

Za każdym razem, gdy dokonasz zmiany w klasie i automatyczne odświeżanie nie jest wyłączone, Latte automatycznie przekompiluje Twoje szablony.

Tworzenie Rozszerzenia

Aby utworzyć własne rozszerzenie, musisz utworzyć klasę, która dziedziczy z Latte\Extension. Aby zobaczyć, jak wygląda takie rozszerzenie, spójrz na wbudowane CoreExtension.

Przyjrzyjmy się metodom, które możesz zaimplementować:

beforeCompile (Latte\Engine $engine)void

Wywoływane przed kompilacją szablonu. Metoda może być używana na przykład do inicjalizacji związanych z kompilacją.

getTags(): array

Wywoływane podczas kompilacji szablonu. Zwraca tablicę asocjacyjną nazwa znacznika ⇒ obiekt wywoływalny, które są funkcjami do parsowania znaczników. Więcej informacji.

public function getTags(): array
{
	return [
		'foo' => FooNode::create(...),
		'bar' => BarNode::create(...),
		'n:baz' => NBazNode::create(...),
		// ...
	];
}

Znacznik n:baz reprezentuje czysty n:atrybut, czyli znacznik, który można zapisać tylko jako atrybut.

Dla znaczników foo i bar Latte automatycznie rozpozna, czy są to znaczniki parzyste, a jeśli tak, można je automatycznie zapisywać za pomocą n:atrybutów, w tym wariantów z prefiksami n:inner-foo i n:tag-foo.

Kolejność wykonywania takich n:atrybutów jest określona przez ich kolejność w tablicy zwróconej przez metodę getTags(). Zatem n:foo jest zawsze wykonywany przed n:bar, nawet jeśli atrybuty w znaczniku HTML są wymienione w odwrotnej kolejności, jak <div n:bar="..." n:foo="...">.

Jeśli potrzebujesz określić kolejność n:atrybutów w wielu rozszerzeniach, użyj metody pomocniczej order(), gdzie parametr before xor after określa, które znaczniki są sortowane przed lub za znacznikiem.

public function getTags(): array
{
	return [
		'foo' => self::order(FooNode::create(...), before: 'bar'),
		'bar' => self::order(BarNode::create(...), after: ['block', 'snippet']),
	];
}

getPasses(): array

Wywoływane podczas kompilacji szablonu. Zwraca tablicę asocjacyjną nazwa przejścia ⇒ obiekt wywoływalny, które są funkcjami reprezentującymi tzw. przejścia kompilatora, które przechodzą i modyfikują AST.

Tutaj również można użyć metody pomocniczej order(). Wartość parametrów before lub after może być * o znaczeniu przed/po wszystkich.

public function getPasses(): array
{
	return [
		'optimize' => Passes::optimizePass(...),
		'sandbox' => self::order($this->sandboxPass(...), before: '*'),
		// ...
	];
}

beforeRender (Latte\Engine $engine)void

Wywoływane przed każdym renderowaniem szablonu. Metoda może być używana na przykład do inicjalizacji zmiennych używanych podczas renderowania.

getFilters(): array

Wywoływane przed renderowaniem szablonu. Zwraca filtry jako tablicę asocjacyjną nazwa filtra ⇒ obiekt wywoływalny. Więcej informacji.

public function getFilters(): array
{
	return [
		'batch' => $this->batchFilter(...),
		'trim' => $this->trimFilter(...),
		// ...
	];
}

getFunctions(): array

Wywoływane przed renderowaniem szablonu. Zwraca funkcje jako tablicę asocjacyjną nazwa funkcji ⇒ obiekt wywoływalny. Więcej informacji.

public function getFunctions(): array
{
	return [
		'clamp' => $this->clampFunction(...),
		'divisibleBy' => $this->divisibleByFunction(...),
		// ...
	];
}

getProviders(): array

Wywoływane przed renderowaniem szablonu. Zwraca tablicę dostawców, którzy są zazwyczaj obiektami używanymi przez znaczniki w czasie działania. Dostęp do nich uzyskuje się przez $this->global->.... Więcej informacji.

public function getProviders(): array
{
	return [
		'myFoo' => $this->foo,
		'myBar' => $this->bar,
		// ...
	];
}

getCacheKey (Latte\Engine $engine)mixed

Wywoływane przed renderowaniem szablonu. Wartość zwracana staje się częścią klucza, którego hash jest zawarty w nazwie pliku skompilowanego szablonu. Dla różnych wartości zwracanych Latte wygeneruje więc różne pliki cache.

wersja: 3.0