Nette Documentation Preview

syntax
Практика для розробників
************************


Інсталяція .[#toc-installation]
===============================

Найкращим способом встановлення Latte є використання Composer:

```shell
composer require latte/latte
```

Підтримувані версії PHP (стосується останніх версій Latte з патчем):

| версія | сумісна з PHP
|-----------------|-------------------
| Latte 3.0 | PHP 8.0 - 8.2


Як відрендерити шаблон .[#toc-how-to-render-a-template]
=======================================================

Як відрендерити шаблон? Просто використовуйте цей простий код:

```php
$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);
```

Параметри можуть бути масивами, а ще краще [об'єктами |#Parameters as a class], що забезпечить перевірку типів і підказки в редакторі.

.[note]
Ви також можете знайти приклади використання у репозиторії [Latte examples |https://github.com/nette-examples/latte].


Продуктивність та кешування .[#toc-performance-and-caching]
===========================================================

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

Кеш автоматично регенерується щоразу, коли ви змінюєте вихідний файл. Таким чином, ви можете зручно редагувати свої Latte-шаблони під час розробки і бачити зміни одразу в браузері. У виробничому середовищі ви можете вимкнути цю функцію і заощадити трохи продуктивності:

```php
$latte->setAutoRefresh(false);
```

При розгортанні на виробничому сервері початкова генерація кешу, особливо для великих додатків, зі зрозумілих причин може зайняти деякий час. Latte має вбудований захист від "переповнення кешу:https://en.wikipedia.org/wiki/Cache_stampede".
Це ситуація, коли сервер отримує велику кількість одночасних запитів, і оскільки кеш Latte ще не існує, всі вони будуть генерувати його одночасно. Що призводить до перевантаження процесора.
Latte розумний, і коли є кілька одночасних запитів, тільки перший потік генерує кеш, інші чекають, а потім використовують його.


Параметри як клас .[#toc-parameters-as-a-class]
===============================================

Краще, ніж передавати змінні в шаблон у вигляді масивів, створити клас. Ви отримуєте [типо-безпечну нотацію |type-system], гарну [пропозицію в IDE |recipes#Editors and IDE] та спосіб [реєстрації фільтрів |extending-latte#Filters Using the Class] і [функцій |extending-latte#Functions Using the Class].

```php
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,
));
```


Вимкнення автоматичного екранування змінних .[#toc-disabling-auto-escaping-of-variable]
=======================================================================================

Якщо змінна містить HTML-рядок, ви можете позначити її так, щоб Latte автоматично (і, відповідно, двічі) не екранував її. Це дозволяє уникнути необхідності вказувати `|noescape` у шаблоні.

Найпростіший спосіб - обернути рядок в об'єкт `Latte\Runtime\Html`:

```php
$params = [
	'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];
```

Latte також не екранує всі об'єкти, які реалізують інтерфейс `Latte\HtmlStringable`. Таким чином, ви можете створити власний клас, метод `__toString()` якого повертатиме HTML-код, який не буде екрановано автоматично:

```php
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'),
];
```

.[warning]
Метод `__toString` повинен повертати коректний HTML і забезпечувати екранування параметрів, інакше може виникнути XSS-уразливість!


Як розширити латте за допомогою фільтрів, тегів і т.д. .[#toc-how-to-extend-latte-with-filters-tags-etc]
========================================================================================================

Як додати до Latte власний фільтр, функцію, тег тощо? Дізнайтеся у розділі про [розширення Latte |extending Latte].
Якщо ви хочете повторно використовувати ваші зміни у різних проектах або поділитися ними з іншими, вам слід [створити розширення |creating-extension].


Будь-який код у шаблоні `{php ...}` .{toc: RawPhpExtension}
===========================================================

Усередині тегу PHP можна писати тільки PHP-вирази, тому ви не можете вставляти в нього конструкції, що закінчуються комою. [`{do}` |tags#do] тому ви не можете, наприклад, вставляти конструкції на кшталт `if ... else` або оператори, що закінчуються крапкою з комою.

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

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


Перевірка згенерованого коду .[#toc-checking-generated-code]{data-version:3.0.7}
================================================================================

Latte компілює шаблони в PHP-код. Звичайно, це гарантує, що згенерований код є синтаксично правильним. Однак, при використанні сторонніх розширень або RawPhpExtension, Latte не може гарантувати коректність згенерованого файлу.
Також в PHP можна написати синтаксично правильний, але заборонений код (наприклад, присвоєння значення змінній $this), який викликає помилку компіляції PHP.
Якщо ви напишете таку операцію в шаблоні, вона також буде включена в згенерований PHP-код. Оскільки в PHP існує більше двохсот різних заборонених операцій, Latte не ставить за мету їх виявлення. PHP сам позначить їх під час рендерингу, що зазвичай не викликає проблем.

Однак бувають ситуації, коли під час компіляції шаблону ви хочете знати, що він не містить помилок компіляції PHP. Особливо, коли шаблони можуть редагуватися користувачами, або ви використовуєте [пісочницю |Sandbox]. У такому випадку перевірте шаблони під час компіляції.
Ви можете активувати цю функціональність за допомогою методу Engine::enablePhpLint(). Оскільки для перевірки потрібно викликати бінарний файл PHP, передайте шлях до нього як параметр:

```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();
}
```


Суворий режим .[#toc-strict-mode]{data-version:3.0.8}
=====================================================

У режимі суворого синтаксичного аналізу Latte перевіряє наявність відсутніх закриваючих HTML-тегів, а також відключає використання змінної `$this`. Щоб увімкнути його:

```php
$latte = new Latte\Engine;
$latte->setStrictParsing();
```

Щоб генерувати шаблони з заголовком `declare(strict_types=1)`, виконайте наступні дії:

```php
$latte = new Latte\Engine;
$latte->setStrictTypes();
```


Переклад у шаблонах .{toc: TranslatorExtension}
===============================================

Використовуйте розширення `TranslatorExtension`, щоб додати [`{_...}` |tags#_], [`{translate}` |tags#translate] і фільтр [`translate` |filters#translate] до шаблону. Вони використовуються для перекладу значень або частин шаблону на інші мови. Параметром є метод (PHP-скрипт), який виконує переклад:

```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 може перекладати всі статичні тексти під час компіляції шаблону. Це економить продуктивність, оскільки кожен рядок перекладається лише один раз, а результат перекладу записується до скомпільованого файлу. Це створює кілька скомпільованих версій шаблону в кеш-пам'яті, по одній для кожної мови. Для цього вам потрібно лише вказати мову як другий параметр:

```php
$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...),
	$lang,
);
```

Під статичним текстом мається на увазі, наприклад, `{_'hello'}` або `{translate}hello{/translate}`. Нестатичний текст, наприклад, `{_$foo}`, буде продовжувати перекладатися під час виконання.

Шаблон також може передавати додаткові параметри перекладачеві через `{_$original, foo: bar}` або `{translate foo: bar}`, які він отримує у вигляді масиву `$params`:

```php
public function translate(string $original, ...$params): string
{
	// $params['foo'] === 'bar'
}
```


Налагодження та трейси .[#toc-debugging-and-tracy]
==================================================

Latte намагається зробити розробку якомога приємнішою. Для налагодження існують три теги [`{dump}` |tags#dump], [`{debugbreak}` |tags#debugbreak] і [`{trace}` |tags#trace].

Ви отримаєте максимальний комфорт, якщо встановите чудовий [інструмент для налагодження Tracy |tracy:] і активуєте плагін Latte:

```php
// вмикає Tracy
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// активує розширення Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
```

Тепер ви побачите всі помилки на акуратному червоному екрані, включаючи помилки в шаблонах з підсвічуванням рядків і стовпців[(відео |https://github.com/nette/tracy/releases/tag/v2.9.0]).
При цьому в правому нижньому куті в так званому Tracy Bar з'являється вкладка для Latte, де ви можете чітко бачити всі відрендеринговані шаблони і їх взаємозв'язки (в тому числі з можливістю кліка в шаблон або скомпільований код), а також змінні:

[* latte-debugging.webp *]

Оскільки Latte компілює шаблони в читабельний PHP-код, ви можете зручно переміщатися по ним у вашій IDE.


Linter: Перевірка синтаксису шаблону .{toc: Linter}
===================================================

Інструмент Linter допоможе вам переглянути всі шаблони і перевірити їх на наявність синтаксичних помилок. Він запускається з консолі:

```shell
vendor/bin/latte-lint <path>
```

Використовуйте параметр `--strict` для активації [суворого режиму#суворий режим |#strict mode].

Якщо ви використовуєте кастомні теги, також створіть свій власний Linter, наприклад, `custom-latte-lint`:

```php
#!/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`:

```php
$latte = new Latte\Engine;
// тут ми налаштовуємо об'єкт $latte
$linter = new Latte\Tools\Linter(engine: $latte);
```


Завантаження шаблонів з рядка .[#toc-loading-templates-from-a-string]
=====================================================================

Потрібно завантажити шаблони з рядків, а не з файлів, можливо, для тестування? [StringLoader |extending-latte#stringloader] допоможе вам:

```php
$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file', $params);
```


Обробник винятків .[#toc-exception-handler]
===========================================

Ви можете визначити власний обробник для очікуваних винятків. Йому передаються винятки, згенеровані всередині [`{try}` |tags#try] і в [пісочниці |sandbox], передаються до нього.

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

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


Автоматичний пошук макетів .[#toc-automatic-layout-lookup]
==========================================================

За допомогою тегу [`{layout}` |template-inheritance#layout-inheritance]шаблон визначає свій батьківський шаблон. Також можна налаштувати автоматичний пошук макета, що спростить написання шаблонів, оскільки їм не потрібно буде включати тег `{layout}`.

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

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

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

Якщо шаблон не повинен мати макет, він вказує на це тегом `{layout none}`.

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

Інсталяція

Найкращим способом встановлення 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 має вбудований захист від „переповнення кешу:https://en.wikipedia.org/…che_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. Якщо ви хочете повторно використовувати ваші зміни у різних проектах або поділитися ними з іншими, вам слід створити розширення.

Будь-який код у шаблоні {php ...}

Усередині тегу PHP можна писати тільки PHP-вирази, тому ви не можете вставляти в нього конструкції, що закінчуються комою. {do} тому ви не можете, наприклад, вставляти конструкції на кшталт if ... else або оператори, що закінчуються крапкою з комою.

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

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

Перевірка згенерованого коду

Latte компілює шаблони в PHP-код. Звичайно, це гарантує, що згенерований код є синтаксично правильним. Однак, при використанні сторонніх розширень або RawPhpExtension, Latte не може гарантувати коректність згенерованого файлу. Також в PHP можна написати синтаксично правильний, але заборонений код (наприклад, присвоєння значення змінній $this), який викликає помилку компіляції PHP. Якщо ви напишете таку операцію в шаблоні, вона також буде включена в згенерований PHP-код. Оскільки в PHP існує більше двохсот різних заборонених операцій, Latte не ставить за мету їх виявлення. PHP сам позначить їх під час рендерингу, що зазвичай не викликає проблем.

Однак бувають ситуації, коли під час компіляції шаблону ви хочете знати, що він не містить помилок компіляції PHP. Особливо, коли шаблони можуть редагуватися користувачами, або ви використовуєте пісочницю. У такому випадку перевірте шаблони під час компіляції. Ви можете активувати цю функціональність за допомогою методу 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 перевіряє наявність відсутніх закриваючих 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
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 <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}.