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

Установка

Лучший способ установки 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 = [ /* 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);

Проверка сгенерированного кода

Latte компилирует шаблоны в PHP-код. Естественно, он гарантирует, что сгенерированный код синтаксически корректен. Однако при использовании сторонних расширений или RawPhpExtension Latte не может гарантировать корректность сгенерированного файла. Кроме того, в PHP можно написать синтаксически корректный, но запрещенный код (например, присвоение значения переменной $this), который вызывает ошибку PHP Compile Error. Если написать такую операцию в шаблоне, то она будет включена и в сгенерированный PHP-код. Поскольку в PHP существует более двухсот различных запрещенных операций, Latte не ставит перед собой задачу их обнаружения. PHP сам будет отмечать их при рендеринге, что обычно не вызывает проблем.

Однако бывают ситуации, когда во время компиляции шаблона необходимо знать, что он не содержит ошибок компиляции PHP. Особенно если шаблоны могут редактироваться пользователями, или вы используете Sandbox. В этом случае необходимо проверять шаблоны во время компиляции. Активировать эту функциональность можно с помощью метода Engine::enablePhpLint(). Поскольку для проверки необходимо вызвать бинарный файл PHP, передайте в качестве параметра его путь:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// ловит ошибки Latte, а также ошибки компиляции в PHP
	echo 'Error: ' . $e->getMessage();
}

Местность

Latte позволяет установить локаль, которая влияет на форматирование чисел, дат и сортировку. Она задается с помощью метода setLocale(). Идентификатор локали соответствует стандарту языковых тегов IETF, который использует расширение PHP intl. Он состоит из кода языка и, возможно, кода страны, например, en_US для английского языка в США, de_DE для немецкого языка в Германии и т.д.

$latte = new Latte\Engine;
$latte->setLocale('cs');

Настройка локали влияет на фильтры 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), который выполняет перевод:

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>

Для активации строгого режима используйте параметр --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:

$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}.

версия: 3.0