Procedury deweloperskie
Instalacja
Najlepszym sposobem na zainstalowanie Latte jest użycie Composera:
composer require latte/latte
Obsługiwane wersje PHP (dotyczy najnowszych wersji Latte):
wersja | kompatybilny z PHP |
---|---|
Latte 3.0 | PHP 8.0 – 8.2 |
Jak renderować szablon
Jak wyrenderować szablon? Wystarczy ten prosty kod:
$latte = new Latte\Engine;
// katalog dla cache
$latte->setTempDirectory('/path/to/tempdir');
$params = [ /* zmienne szablonu */ ];
// lub $params = new TemplateParameters(/* ... */);
// renderuj na wyjście
$latte->render('template.latte', $params);
// renderuj do zmiennej
$output = $latte->renderToString('template.latte', $params);
Parametry mogą być tablicą lub, jeszcze lepiej, obiektem, który zapewni kontrolę typów i podpowiadanie w edytorach.
Przykłady użycia można znaleźć również w repozytorium Przykłady Latte.
Wydajność i cache
Szablony w Latte są niezwykle szybkie, ponieważ Latte kompiluje je bezpośrednio do kodu PHP i zapisuje w pamięci podręcznej na dysku. Dzięki temu nie mają żadnego dodatkowego narzutu w porównaniu do szablonów napisanych w czystym PHP.
Cache jest automatycznie regenerowany za każdym razem, gdy zmienisz plik źródłowy. Podczas rozwoju możesz więc wygodnie edytować szablony w Latte, a zmiany natychmiast widzieć w przeglądarce. Możesz wyłączyć tę funkcję w środowisku produkcyjnym, aby zaoszczędzić trochę wydajności:
$latte->setAutoRefresh(false);
Podczas wdrażania na serwerze produkcyjnym, początkowe wygenerowanie cache, zwłaszcza w przypadku większych aplikacji, może oczywiście chwilę potrwać. Latte ma wbudowaną prewencję przed Cache_stampede. Jest to sytuacja, w której pojawia się większa liczba jednoczesnych żądań uruchamiających Latte, a ponieważ cache jeszcze nie istnieje, wszystkie zaczęłyby go generować jednocześnie. Co nieproporcjonalnie obciążyłoby serwer. Latte jest sprytne i przy wielu jednoczesnych żądaniach cache generuje tylko pierwszy wątek, pozostałe czekają, a następnie go wykorzystują.
Parametry jako klasa
Lepiej niż przekazywać zmienne do szablonu jako tablicę jest utworzyć klasę. Zyskasz w ten sposób zapis bezpieczny typowo, przyjemne podpowiadanie w IDE oraz drogę do rejestracji filtrów i funkcji.
class MailTemplateParameters
{
public function __construct(
public string $lang,
public Address $address,
public string $subject,
public array $items,
public ?float $price = null,
) {}
}
$latte->render('mail.latte', new MailTemplateParameters(
lang: $this->lang,
subject: $title,
price: $this->getPrice(),
items: [],
address: $userAddress,
));
Wyłączanie automatycznego escapowania zmiennej
Jeśli zmienna zawiera ciąg znaków w HTML, możesz ją oznaczyć tak, aby Latte automatycznie (a więc podwójnie) jej nie
escapowało. Unikniesz w ten sposób konieczności używania w szablonie |noescape
.
Najprostszym sposobem jest opakowanie ciągu znaków w obiekt Latte\Runtime\Html
:
$params = [
'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];
Latte ponadto nie escapuje wszystkich obiektów, które implementują interfejs Latte\HtmlStringable
. Możesz
więc stworzyć własną klasę, której metoda __toString()
będzie zwracać kod HTML, który nie będzie
automatycznie escapowany:
class Emphasis extends Latte\HtmlStringable
{
public function __construct(
private string $str,
) {
}
public function __toString(): string
{
return '<em>' . htmlspecialchars($this->str) . '</em>';
}
}
$params = [
'foo' => new Emphasis('hello'),
];
Metoda __toString
musi zwracać poprawny HTML i zapewniać escapowanie parametrów, w przeciwnym
razie może dojść do podatności XSS!
Jak rozszerzyć Latte o filtry, znaczniki itp.
Jak dodać do Latte własny filtr, funkcję, znacznik itp.? O tym traktuje rozdział rozszerzanie Latte. Jeśli chcesz ponownie wykorzystać swoje modyfikacje w różnych projektach lub podzielić się nimi z innymi, powinieneś utworzyć rozszerzenie.
Dowolny kod w szablonie {php ...}
Wewnątrz znacznika {do}
można zapisywać tylko wyrażenia
PHP, nie można więc na przykład wstawiać konstrukcji takich jak if ... else
ani instrukcji zakończonych
średnikiem.
Można jednak zarejestrować rozszerzenie RawPhpExtension
, które dodaje znacznik {php ...}
. Za jego
pomocą można wstawiać dowolny kod PHP. Nie obowiązują go żadne zasady trybu sandbox, użycie jest więc na
odpowiedzialność autora szablonu.
$latte->addExtension(new Latte\Essential\RawPhpExtension);
Kontrola wygenerowanego kodu
Latte kompiluje szablony do kodu PHP. Oczywiście dba o to, aby wygenerowany kod był składniowo poprawny. Jednak przy
użyciu rozszerzeń stron trzecich lub RawPhpExtension
Latte nie może zagwarantować poprawności wygenerowanego
pliku. Można również napisać w PHP kod, który jest wprawdzie składniowo poprawny, ale jest zabroniony (na przykład
przypisanie wartości do zmiennej $this
) i spowoduje PHP Compile Error. Jeśli taką operację zapiszesz w
szablonie, trafi ona również do wygenerowanego kodu PHP. Ponieważ w PHP istnieje ponad dwieście różnych zabronionych
operacji, Latte nie ma ambicji ich wykrywać. Zwróci na nie uwagę dopiero samo PHP podczas renderowania, co zazwyczaj niczemu
nie szkodzi.
Są jednak sytuacje, w których chcesz wiedzieć już w momencie kompilacji szablonu, że nie zawiera on żadnego PHP Compile
Error. Zwłaszcza wtedy, gdy szablony mogą edytować użytkownicy lub używasz Sandbox. W takim przypadku zlecaj kontrolę szablonów już w czasie kompilacji. Tę
funkcjonalność włączysz metodą Engine::enablePhpLint()
. Ponieważ do kontroli potrzebuje wywołać binarkę PHP,
przekaż do niej ścieżkę jako parametr:
$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');
try {
$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
// przechwytuje błędy w Latte, a także Compile Error w PHP
echo 'Błąd: ' . $e->getMessage();
}
Ustawienia regionalne
Latte pozwala ustawić ustawienia regionalne (locale), które wpływają na formatowanie liczb, dat i sortowanie. Ustawia się
je za pomocą metody setLocale()
. Identyfikator ustawień regionalnych jest zgodny ze standardem IETF language tag,
który używa rozszerzenie PHP intl
. Składa się z kodu języka i opcjonalnie kodu kraju, np. en_US
dla angielskiego w Stanach Zjednoczonych, de_DE
dla niemieckiego w Niemczech, pl_PL
dla polskiego w
Polsce itp.
$latte = new Latte\Engine;
$latte->setLocale('pl_PL');
Ustawienie locale wpływa na filtry localDate, sort, number oraz bytes.
Wymaga rozszerzenia PHP intl
. Ustawienie w Latte nie wpływa na globalne ustawienia locale
w PHP.
Tryb ścisły
W trybie ścisłego parsowania Latte kontroluje, czy nie brakuje zamykających znaczników HTML, a także zabrania używania
zmiennej $this
. Włączysz go w ten sposób:
$latte = new Latte\Engine;
$latte->setStrictParsing();
Generowanie szablonów z nagłówkiem declare(strict_types=1)
włączysz w ten sposób:
$latte = new Latte\Engine;
$latte->setStrictTypes();
Tłumaczenie w szablonach
Za pomocą rozszerzenia TranslatorExtension
dodasz do szablonu znaczniki {_...}
, {translate}
oraz filtr translate
. Służą one do tłumaczenia wartości lub
części szablonu na inne języki. Jako parametr podajemy metodę (PHP callable) wykonującą tłumaczenie:
class MyTranslator
{
public function __construct(private string $lang)
{}
public function translate(string $original): string
{
// z $original tworzymy $translated zgodnie z $this->lang
return $translated;
}
}
$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...), // [$translator, 'translate'] w PHP 8.0
);
$latte->addExtension($extension);
Translator jest wywoływany w czasie działania podczas renderowania szablonu. Latte potrafi jednak wszystkie statyczne teksty tłumaczyć już podczas kompilacji szablonu. Oszczędza to wydajność, ponieważ każdy ciąg znaków jest tłumaczony tylko raz, a wynikowe tłumaczenie jest zapisywane w skompilowanej postaci. W katalogu z cache powstaje więc więcej skompilowanych wersji szablonu, jedna dla każdego języka. W tym celu wystarczy podać język jako drugi parametr:
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...),
$lang,
);
Tekstem statycznym jest na przykład {_'hello'}
lub {translate}hello{/translate}
. Teksty
niestatyczne, jak na przykład {_$foo}
, nadal będą tłumaczone w czasie działania.
Tłumaczowi można z szablonu przekazywać również dodatkowe parametry za pomocą {_$original, foo: bar}
lub
{translate foo: bar}
, które otrzyma jako tablicę $params
:
public function translate(string $original, ...$params): string
{
// $params['foo'] === 'bar'
}
Debugowanie i Tracy
Latte stara się jak najbardziej ułatwić Ci rozwój. Bezpośrednio do celów debugowania istnieje trójka znaczników {dump}
, {debugbreak}
oraz {trace}
.
Największy komfort uzyskasz, jeśli zainstalujesz jeszcze świetne narzędzie do debugowania Tracy i aktywujesz dodatek dla Latte:
// włącza Tracy
Tracy\Debugger::enable();
$latte = new Latte\Engine;
// aktywuje rozszerzenie dla Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
Teraz wszystkie błędy będą wyświetlane na przejrzystym czerwonym ekranie, w tym błędy w szablonach z podświetleniem wiersza i kolumny (wideo). Jednocześnie w prawym dolnym rogu, w tzw. Tracy Barze, pojawi się zakładka dla Latte, gdzie przejrzyście widać wszystkie renderowane szablony i ich wzajemne relacje (włącznie z możliwością przejścia do szablonu lub skompilowanego kodu) oraz zmienne:

Ponieważ Latte kompiluje szablony do przejrzystego kodu PHP, możesz je wygodnie krokować w swoim IDE.
Linter: walidacja składni szablonów
Przejrzeć wszystkie szablony i sprawdzić, czy nie zawierają błędów składniowych, pomoże Ci narzędzie Linter. Uruchamia się je z konsoli:
vendor/bin/latte-lint <ścieżka>
Parametrem --strict
aktywujesz tryb ścisły.
Jeśli używasz własnych znaczników, utwórz również własną wersję Lintera, np. custom-latte-lint
:
#!/usr/bin/env php
<?php
// podaj rzeczywistą ścieżkę do pliku autoload.php
require __DIR__ . '/vendor/autoload.php';
$path = $argv[1] ?? '.';
$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// tutaj dodaj swoje poszczególne rozszerzenia
$latte->addExtension(/* ... */);
$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);
Alternatywnie możesz przekazać własny obiekt Latte\Engine
do Lintera:
$latte = new Latte\Engine;
// tutaj konfigurujemy obiekt $latte
$linter = new Latte\Tools\Linter(engine: $latte);
Ładowanie szablonów z ciągu znaków
Potrzebujesz ładować szablony z ciągów znaków zamiast plików, na przykład do celów testowania? Pomoże Ci StringLoader:
$latte->setLoader(new Latte\Loaders\StringLoader([
'main.file' => '{include other.file}',
'other.file' => '{if true} {$var} {/if}',
]));
$latte->render('main.file', $params);
Obsługa wyjątków
Możesz zdefiniować własny handler do obsługi oczekiwanych wyjątków. Przekazane zostaną mu wyjątki powstałe wewnątrz
{try}
oraz w sandboxie.
$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
$logger->log($e);
};
$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);
Automatyczne wyszukiwanie layoutu
Za pomocą znacznika {layout}
szablon określa swój
szablon nadrzędny. Możliwe jest również automatyczne wyszukiwanie layoutu, co uprości pisanie szablonów, ponieważ nie
będzie w nich konieczne używanie znacznika {layout}
.
Osiąga się to w następujący sposób:
$finder = function (Latte\Runtime\Template $template) {
if (!$template->getReferenceType()) {
// zwraca ścieżkę do pliku layoutu
return 'automatic.layout.latte';
}
};
$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);
Jeśli szablon nie ma mieć layoutu, informuje o tym znacznikiem {layout none}
.