Практики розробки

Встановлення

Найкращий спосіб встановити 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, і зміни одразу бачите в браузері. Цю функцію можна вимкнути в робочому середовищі та заощадити трохи продуктивності:

$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} можна записувати лише PHP-вирази, тому ви не можете, наприклад, вставити конструкції типу if ... else або оператори, що закінчуються крапкою з комою.

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

$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('uk_UA');

Налаштування середовища впливає на фільтри localDate, sort, number та bytes.

Вимагає PHP-розширення intl. Налаштування в Latte не впливає на глобальні налаштування locale в 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 Bar з'явиться вкладка для 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} та в sandbox.

$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
	$logger->log($e);
};

$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);

Автоматичний пошук layout

За допомогою тегу {layout} шаблон визначає свій батьківський шаблон. Також можливо дозволити автоматичний пошук layout, що спростить написання шаблонів, оскільки в них не потрібно буде вказувати тег {layout}.

Цього можна досягти наступним чином:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// повертає шлях до файлу з layout
		return 'automatic.layout.latte';
	}
};

$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);

Якщо шаблон не повинен мати layout, він повідомить про це тегом {layout none}.

версія: 3.0