Практика для разработчиков

Установка

Лучший способ установки Latte – это использование Composer:

composer require latte/latte

Поддерживаемые версии PHP (применяется к последним версиям патча Latte):

версия совместимая с PHP
Latte 3.0 PHP 8.0 – 8.2
Latte 2.11 PHP 7.1 – 8.2
Latte 2.8 – 2.10 PHP 7.1 – 8.1

Как рендерить шаблон

Как отобразить шаблон? Просто используйте этот простой код:

$latte = new Latte\Engine;
// каталог кэша
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* template variables */ ];
// или $params = new TemplateParameters(/* ... */);

// рендеринг в вывод
$latte->render('template.latte', $params);
// или вывести в переменную
$output = $latte->renderToString('template.latte', $params);

Параметрами могут быть массивы или еще лучше объекты, что обеспечит проверку типов и предложение в редакторе.

Примеры использования вы также можете найти в репозитории Latte examples.

Производительность и кэширование

Шаблоны Latte чрезвычайно быстры, поскольку Latte компилирует их непосредственно в PHP-код и кэширует на диске. Таким образом, они не имеют дополнительных накладных расходов по сравнению с шаблонами, написанными на чистом PHP.

Кэш автоматически обновляется каждый раз, когда вы изменяете исходный файл. Таким образом, вы можете удобно редактировать шаблоны Latte во время разработки и сразу же видеть изменения в браузере. Вы можете отключить эту функцию в производственной среде и сэкономить немного производительности:

$latte->setAutoRefresh(false);

При развертывании на рабочем сервере начальная генерация кэша, особенно для больших приложений, может занять некоторое время. 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} поэтому вы не можете, например, вставлять такие конструкции, как if ... else или утверждения, завершаемые точкой с запятой.

Однако вы можете зарегистрировать расширение RawPhpExtension, которое добавляет тег {php ...}, который может быть использован для вставки любого PHP-кода на страх и риск автора шаблона.

$latte->addExtension(new Latte\Essential\RawPhpExtension);

Перевод в шаблонах

Используйте расширение TranslatorExtension, чтобы добавить {_...}, {translate} и фильтр translate в шаблон. Они используются для перевода значений или частей шаблона на другие языки. Параметр – это метод (вызываемый PHP), который выполняет перевод:

class MyTranslator
{
	public function __construct(private string $lang)
	{}

	public function translate(string $original): string
	{
		// создать $translated из $original в соответствии с $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'
}

Отладка и трассировка

Latte старается сделать разработку как можно более приятной. Для целей отладки существует три тега {dump}, {debugbreak} и {trace}.

Вы получите максимальный комфорт, если установите замечательный инструмент отладки Tracy и активируете плагин Latte:

// включает Трейси
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// активирует расширение Трейси
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

Теперь вы будете видеть все ошибки на аккуратном красном экране, включая ошибки в шаблонах с подсветкой строк и столбцов(видео). В то же время в правом нижнем углу, в так называемой панели Tracy, появится вкладка для Latte, где вы сможете наглядно увидеть все отрисованные шаблоны и их взаимосвязи (включая возможность щелкнуть в шаблон или скомпилированный код), а также переменные:

Поскольку Latte компилирует шаблоны в читаемый PHP-код, вы можете удобно просматривать их в вашей IDE.

Linter: Проверка синтаксиса шаблона

Инструмент Linter поможет вам просмотреть все шаблоны и проверить их на наличие синтаксических ошибок. Он запускается из консоли:

vendor/bin/latte-lint <path>

Если вы используете пользовательские теги, также создайте свой пользовательский Linter, например, custom-latte-lint:

#!/usr/bin/env php
<?php

// введите фактический путь к файлу autoload.php
require __DIR__ . '/vendor/autoload.php';

$linter = new Latte\Tools\Linter($engine);
$linter->scanDirectory($path);

$engine = new Latte\Engine;
// регистрирует отдельные расширения здесь
$engine->addExtension(/* ... */);

$path = $argv[1];
$linter = new Latte\Tools\Linter(engine: $engine);
$ok = $linter->scanDirectory($path);
exit($ok ? 0: 1);

Загрузка шаблонов из строки

Вам нужно загрузить шаблоны из строк, а не из файлов, возможно, в целях тестирования? 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}.