Практики разработки
Установка
Лучший способ установить Latte — это с помощью Composer:
composer require latte/latte
Поддерживаемые версии PHP (применяется к последним патч-версиям Latte):
версия | совместимо с PHP |
---|---|
Latte 3.0 | PHP 8.0 – 8.2 |
Как отобразить шаблон
Как отобразить шаблон? Для этого достаточно этого простого кода:
$latte = new Latte\Engine;
// каталог для кеша
$latte->setTempDirectory('/path/to/tempdir');
$params = [ /* переменные шаблона */ ];
// or $params = new TemplateParameters(/* ... */);
// рендерить на вывод
$latte->render('template.latte', $params);
// рендерить в переменную
$output = $latte->renderToString('template.latte', $params);
Параметры могут быть массивом или, еще лучше, объектом, который обеспечит проверку типов и автодополнение в редакторах.
Примеры использования вы также найдете в репозитории Latte examples.
Производительность и кеш
Шаблоны в Latte чрезвычайно быстры, так как Latte компилирует их прямо в PHP-код и сохраняет в кеше на диск. Таким образом, у них нет никаких дополнительных накладных расходов по сравнению с шаблонами, написанными на чистом PHP.
Кеш автоматически регенерируется каждый раз, когда вы изменяете исходный файл. Во время разработки вы можете удобно редактировать шаблоны в Latte, и изменения сразу же видны в браузере. Эту функцию можно отключить в production-среде и сэкономить немного производительности:
$latte->setAutoRefresh(false);
При развертывании на production-сервере первоначальная генерация кеша, особенно для более крупных приложений, может, естественно, занять некоторое время. Latte имеет встроенное предотвращение cache stampede. Это ситуация, когда одновременно поступает большое количество параллельных запросов, которые запускают Latte, и поскольку кеш еще не существует, все они начали бы генерировать его одновременно. Что непропорционально нагрузило бы сервер. Latte умный и при нескольких параллельных запросах генерирует кеш только первый поток, остальные ждут и впоследствии используют его.
Параметры как класс
Лучше, чем передавать переменные в шаблон как массив, — это создать класс. Вы получите типобезопасную запись, приятное автодополнение в IDE и путь для регистрации фильтров и функций.
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,
));
Отключение автоэкранирования переменной
Если переменная содержит строку в HTML, вы можете пометить ее так, чтобы
Latte автоматически (и, следовательно, дважды) ее не экранировал. Вы
избежите необходимости указывать в шаблоне |noescape
.
Самый простой способ — обернуть строку в объект Latte\Runtime\Html
:
$params = [
'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];
Latte также не экранирует все объекты, которые реализуют интерфейс
Latte\HtmlStringable
. Вы можете создать собственный класс, метод
__toString()
которого будет возвращать HTML-код, который не будет
автоматически экранироваться:
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'),
];
Метод __toString
должен возвращать корректный HTML и
обеспечивать экранирование параметров, иначе может возникнуть
уязвимость XSS!
Как расширить Latte фильтрами, тегами и т.д.
Как добавить в Latte собственный фильтр, функцию, тег и т.д.? Об этом рассказывается в главе расширяем Latte. Если вы хотите повторно использовать свои изменения в разных проектах или поделиться ими с другими, вам следует создать расширение.
Любой код в шаблоне {php ...}
Внутри тега {do}
можно записывать
только PHP-выражения, вы не можете, например, вставить конструкции типа
if ... else
или операторы, завершенные точкой с запятой.
Однако вы можете зарегистрировать расширение RawPhpExtension
,
которое добавляет тег {php ...}
. С его помощью можно вставлять любой
PHP-код. На него не распространяются никакие правила режима песочницы,
поэтому использование лежит на ответственности автора шаблона.
$latte->addExtension(new Latte\Essential\RawPhpExtension);
Проверка сгенерированного кода
Latte компилирует шаблоны в PHP-код. Разумеется, он следит за тем, чтобы
сгенерированный код был синтаксически валидным. Однако при
использовании расширений третьих сторон или RawPhpExtension
Latte не
может гарантировать правильность сгенерированного файла. Также в PHP
можно записать код, который, хотя и синтаксически правильный, но
запрещен (например, присваивание значения переменной $this
) и
вызывает PHP Compile Error. Если вы запишете такую операцию в шаблоне, она
попадет и в сгенерированный PHP-код. Поскольку в PHP существует около двух
сотен различных запрещенных операций, Latte не ставит перед собой задачу
их обнаруживать. На них укажет само PHP при рендеринге, что обычно ничему
не мешает.
Однако бывают ситуации, когда вы хотите знать уже во время компиляции
шаблона, что он не содержит никаких PHP Compile Error. Особенно тогда, когда
шаблоны могут редактировать пользователи, или вы используете Sandbox. В таком случае настройте проверку
шаблонов уже во время компиляции. Эту функциональность можно включить
методом Engine::enablePhpLint()
. Поскольку для проверки требуется
вызывать бинарный файл PHP, передайте путь к нему в качестве
параметра:
$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');
try {
$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
// перехватывает ошибки в Latte, а также Compile Error в PHP
echo 'Error: ' . $e->getMessage();
}
Национальная среда
Latte позволяет установить национальную среду (локаль), которая влияет
на форматирование чисел, дат и сортировку. Она устанавливается с
помощью метода setLocale()
. Идентификатор среды руководствуется
стандартом IETF language tag, который использует расширение PHP intl
. Он
состоит из кода языка и, возможно, кода страны, например, en_US
для
английского языка в Соединенных Штатах, de_DE
для немецкого языка
в Германии и т.д.
$latte = new Latte\Engine;
$latte->setLocale('ru_RU');
Настройка среды влияет на фильтры localDate, sort, number и bytes.
Требует PHP-расширения intl
. Настройка в Latte не влияет на
глобальную настройку локали в PHP.
Строгий режим
В строгом режиме парсинга Latte контролирует, не отсутствуют ли
закрывающие HTML-теги, а также запрещает использование переменной
$this
. Включить его можно так:
$latte = new Latte\Engine;
$latte->setStrictParsing();
Генерацию шаблонов с заголовком declare(strict_types=1)
можно
включить так:
$latte = new Latte\Engine;
$latte->setStrictTypes();
Перевод в шаблонах
С помощью расширения TranslatorExtension
вы добавите в шаблон теги {_...}
, {translate}
и фильтр translate
. Они служат для перевода
значений или частей шаблона на другие языки. В качестве параметра мы
указываем метод (PHP callable), выполняющий перевод:
class MyTranslator
{
public function __construct(private string $lang)
{}
public function translate(string $original): string
{
// из $original создаем $translated в соответствии с $this->lang
return $translated;
}
}
$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...), // [$translator, 'translate'] в PHP 8.0
);
$latte->addExtension($extension);
Переводчик вызывается во время выполнения при рендеринге шаблона. Latte, однако, умеет переводить все статические тексты уже во время компиляции шаблона. Это экономит производительность, так как каждая строка переводится только один раз, и результирующий перевод записывается в скомпилированную форму. В каталоге кеша таким образом создается несколько скомпилированных версий шаблона, одна для каждого языка. Для этого достаточно только указать язык в качестве второго параметра:
$extension = new Latte\Essential\TranslatorExtension(
$translator->translate(...),
$lang,
);
Под статическим текстом подразумевается, например, {_'hello'}
или
{translate}hello{/translate}
. Нестатические тексты, например, {_$foo}
,
по-прежнему будут переводиться во время выполнения.
Переводчику можно из шаблона передавать и дополнительные параметры
с помощью {_$original, foo: bar}
или {translate foo: bar}
, которые он получит
как массив $params
:
public function translate(string $original, ...$params): string
{
// $params['foo'] === 'bar'
}
Отладка и Tracy
Latte старается сделать разработку как можно более приятной.
Непосредственно для целей отладки существует тройка тегов {dump}
, {debugbreak}
и {trace}
.
Наибольший комфорт вы получите, если еще установите замечательный инструмент отладки Tracy и активируете дополнение для Latte:
// включает Tracy
Tracy\Debugger::enable();
$latte = new Latte\Engine;
// активирует расширение для Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
Теперь все ошибки будут отображаться на наглядном красном экране, включая ошибки в шаблонах с подсветкой строки и столбца (видео). Одновременно в правом нижнем углу в так называемом Tracy Baru появится вкладка для Latte, где наглядно видны все рендеримые шаблоны и их взаимные связи (включая возможность перейти по ссылке в шаблон или скомпилированный код), а также переменные:

Поскольку Latte компилирует шаблоны в понятный PHP-код, вы можете удобно пошагово выполнять их в своей IDE.
Linter: валидация синтаксиса шаблонов
Пройти все шаблоны и проверить, не содержат ли они синтаксических ошибок, вам поможет инструмент Linter. Он запускается из консоли:
vendor/bin/latte-lint <путь>
Параметром --strict
активируется строгий
режим.
Если вы используете собственные теги, создайте также собственную
версию Linter, например, custom-latte-lint
:
#!/usr/bin/env php
<?php
// укажите реальный путь к файлу autoload.php
require __DIR__ . '/vendor/autoload.php';
$path = $argv[1] ?? '.';
$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// здесь добавьте отдельные свои расширения
$latte->addExtension(/* ... */);
$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);
Альтернативно вы можете передать собственный объект Latte\Engine
в
Linter:
$latte = new Latte\Engine;
// здесь конфигурируем объект $latte
$linter = new Latte\Tools\Linter(engine: $latte);
Загрузка шаблонов из строки
Нужно загружать шаблоны из строк вместо файлов, например, для целей тестирования? Вам поможет StringLoader:
$latte->setLoader(new Latte\Loaders\StringLoader([
'main.file' => '{include other.file}',
'other.file' => '{if true} {$var} {/if}',
]));
$latte->render('main.file', $params);
Обработчик исключений
Вы можете определить собственный обработчик для ожидаемых
исключений. Ему будут переданы исключения, возникшие внутри {try}
и в песочнице.
$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
$logger->log($e);
};
$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);
Автоматический поиск макета
С помощью тега {layout}
шаблон
определяет свой родительский шаблон. Также возможно настроить
автоматический поиск макета, что упростит написание шаблонов, так как
в них не нужно будет указывать тег {layout}
.
Этого можно достичь следующим способом:
$finder = function (Latte\Runtime\Template $template) {
if (!$template->getReferenceType()) {
// возвращает путь к файлу с макетом
return 'automatic.layout.latte';
}
};
$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);
Если шаблон не должен иметь макет, он сообщит об этом тегом
{layout none}
.