Nette Documentation Preview

syntax
Vývojářské postupy
******************


Instalace
=========

Nejlepší způsob instalace Latte je pomocí Composeru:

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

Podporované verze PHP (platí pro poslední setinkové verze Latte):

| verze           | kompatibilní s PHP
|-----------------|-------------------
| Latte 3.0       | PHP 8.0 – 8.2


Vykreslení šablony
==================

Jak vykreslit šablonu? Stačí k tomu tento jednoduchý kód:

```php
$latte = new Latte\Engine;
// nastavení adresáře pro cache
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* proměnné šablony */ ];
// nebo $params = new TemplateParameters(/* ... */);

// vykreslení na výstup
$latte->render('template.latte', $params);
// vykreslení do proměnné
$output = $latte->renderToString('template.latte', $params);
```

Parametry mohou být pole nebo ještě lépe [objekt|#Parametry jako třída], který zajistí typovou kontrolu a napovídání v editorech.

.[note]
Ukázky použití najdete také v repozitáři [Latte examples |https://github.com/nette-examples/latte].


Výkon a cache
=============

Šablony v Latte jsou extrémně rychlé, protože se kompilují přímo do PHP kódu a ukládají do cache na disk. Nemají tedy žádnou režii navíc oproti šablonám psaným v čistém PHP.

Cache se automaticky regeneruje při změně zdrojového souboru. Během vývoje tedy pohodlně editujete šablony v Latte a změny okamžitě vidíte v prohlížeči. Pro mírné zvýšení výkonu v produkčním prostředí můžete automatickou regeneraci vypnout:

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

Při nasazení na produkční server může prvotní vygenerování cache, zejména u rozsáhlých aplikací, chvíli trvat. Latte má vestavěnou prevenci proti "cache stampede":https://en.wikipedia.org/wiki/Cache_stampede, což je situace, kdy velký počet souběžných požadavků spustí generování cache současně, což by mohlo přetížit server. Latte tento problém řeší chytře - při více souběžných požadavcích generuje cache pouze první vlákno, zatímco ostatní čekají a následně využijí vytvořenou cache.


Parametry jako třída
====================

Místo předávání proměnných do šablony jako pole je lepší vytvořit si třídu. Získáte tak [typově bezpečný zápis|type-system], [příjemné napovídání v IDE|recipes#Editory a IDE] a možnost [registrace filtrů|extending-latte#Filtry pomocí třídy] a [funkcí|extending-latte#Funkce pomocí třídy].

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


Vypnutí auto-escapování proměnné
================================

Pokud proměnná obsahuje HTML řetězec, můžete ji označit tak, aby ji Latte automaticky (a tedy dvojitě) neescapovalo. Tím se vyhnete potřebě uvádět v šabloně `|noescape`.

Nejjednodušší způsob je zabalit řetězec do objektu `Latte\Runtime\Html`:

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

Latte také neescapuje objekty implementující rozhraní `Latte\HtmlStringable`. Můžete si vytvořit vlastní třídu, jejíž metoda `__toString()` bude vracet HTML kód, který se nebude automaticky escapovat:

```php
class Emphasis implements Latte\HtmlStringable
{
	public function __construct(
		private string $str,
	) {
	}

	public function __toString(): string
	{
		return '<em>' . htmlspecialchars($this->str) . '</em>';
	}
}

$params = [
	'foo' => new Emphasis('hello'),
];
```

.[warning]
Metoda `__toString` musí vracet korektní HTML a zajistit escapování parametrů, jinak může dojít ke zranitelnosti XSS!


Jak rozšířit Latte o filtry, značky atd.
========================================

Informace o přidávání vlastních filtrů, funkcí, značek atd. najdete v kapitole [rozšiřujeme Latte |extending-latte].
Pokud chcete své úpravy znovu použít v různých projektech nebo je sdílet s ostatními, měli byste [vytvořit rozšíření |creating-extension].


Libovolný PHP kód v šabloně `{php ...}` .{toc: RawPhpExtension}
===============================================================

Zatímco uvnitř značky [`{do}`|tags#do] lze zapisovat pouze PHP výrazy, rozšíření `RawPhpExtension` přidává značku `{php ...}`, která umožňuje vkládat jakýkoliv PHP kód. Použití je na zodpovědnost autora šablony, protože se na ni nevztahují pravidla sandbox režimu.

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


Kontrola vygenerovaného kódu .{data-version:3.0.7}
==================================================

Latte kompiluje šablony do PHP kódu a dbá na jeho syntaktickou správnost. Nicméně při použití rozšíření třetích stran nebo `RawPhpExtension` nemůže Latte zaručit korektnost vygenerovaného souboru. V PHP lze také zapsat kód, který je syntakticky správný, ale zakázaný (například přiřazení hodnoty do `$this`) a způsobí PHP Compile Error.
Pokud takovou operaci zapíšete v šabloně, dostane se i do vygenerovaného PHP kódu. Jelikož v PHP existují na dvě stovky různých zakázaných operací, nemá Latte ambici je odhalovat. Upozorní na ně až samotné PHP při vykreslení, což obvykle ničemu nevadí.

Pokud chcete odhalit tyto problémy už v době kompilace šablony, zejména pokud šablony mohou editovat uživatelé nebo používáte [Sandbox], můžete zapnout kontrolu pomocí `Engine::enablePhpLint()`. Ke kontrole je potřeba volat binárku PHP, jejíž cestu předáte jako parametr:

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

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// zachytí chyby v Latte a také Compile Error v PHP
	echo 'Error: ' . $e->getMessage();
}
```


Národní prostředí .{data-version:3.0.18}{toc: Locale}
=====================================================

Latte umožňuje nastavit národní prostředí, které ovlivňuje formátování čísel, datumů a řazení. Nastavuje se pomocí metody `setLocale()`. Identifikátor prostředí se řídí standardem IETF language tag, který používá rozšíření PHP `intl`. Skládá se z kódu jazyka a případně kódu země, např. `en_US` pro angličtinu ve Spojených státech, `de_DE` pro němčinu v Německu atd.

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

Nastavení prostředí ovlivňuje filtry [localDate|filters#localDate], [sort|filters#sort], [number|filters#number] a [bytes|filters#bytes].

.[note]
Vyžaduje PHP rozšíření `intl`. Nastavení v Latte neovlivňuje globální nastavení locale v PHP.


Striktní režim .{data-version:3.0.8}
====================================

Ve striktním režimu parsování Latte kontroluje, zda nechybí uzavírací HTML značky a zakazuje používání proměnné `$this`. Zapnete jej takto:

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

Generování šablon s hlavičkou `declare(strict_types=1)` zapnete takto:

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


Překládání v šablonách .{toc: TranslatorExtension}
==================================================

Rozšíření `TranslatorExtension` přidává do šablony značky [`{_...}`|tags#_], [`{translate}`|tags#translate] a filtr [`translate`|filters#translate] pro překládání hodnot nebo částí šablony do jiných jazyků. Jako parametr uvedeme metodu provádějící překlad:

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

	public function translate(string $original): string
	{
		// z $original vytvoříme $translated dle $this->lang
		return $translated;
	}
}

$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...), // [$translator, 'translate'] v PHP 8.0
);
$latte->addExtension($extension);
```

Translator se volá za běhu při vykreslování šablony. Latte ovšem umí všechny statické texty překládat už během kompilace šablony. Tím se ušetří výkon, protože každý řetězec se přeloží jen jednou a výsledný překlad se zapíše do zkompilované podoby. V adresáři s cache tak vznikne více zkompilovaných verzí šablony, jedna pro každý jazyk. K tomu stačí pouze uvést jazyk jako druhý parametr:

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

Statickým textem je myšleno třeba `{_'hello'}` nebo `{translate}hello{/translate}`. Nestatické texty, jako třeba `{_$foo}`, se nadále budou překládat za běhu.

Překladači lze ze šablony předávat i doplňující parametry pomocí `{_$original, foo: bar}` nebo `{translate foo: bar}`, které získá jako pole `$params`:

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


Debuggování a Tracy
===================

Latte se vám snaží vývoj co nejvíce zpříjemnit. Nabízí několik nástrojů pro debugování, včetně značek [`{dump}`|tags#dump], [`{debugbreak}`|tags#debugbreak] a [`{trace}`|tags#trace].

Největší komfort získáte, když ještě si nainstalujete skvělý [ladicí nástroj Tracy|tracy:] a aktivujete doplněk pro Latte:

```php
// zapne Tracy
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// aktivuje rozšíření pro Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
```

Nyní se vám budou všechny chyby zobrazovat v přehledné červené obrazovce, včetně chyb v šablonách se zvýrazněním řádku a sloupce ([video|https://github.com/nette/tracy/releases/tag/v2.9.0]).
Zároveň v pravém dolním rohu v tzv. Tracy Baru se objeví záložka pro Latte, kde jsou přehledně vidět všechny vykreslované šablony a jejich vzájemné vztahy (včetně možnosti se do šablony nebo zkompilovaného kódu prokliknout) a také proměnné:

[* latte-debugging.webp *]

Jelikož Latte kompiluje šablony do přehledného PHP kódu, můžete je pohodlně krokovat ve svém IDE.


Linter: validace syntaxe šablon .{toc: Linter}
==============================================

Pro kontrolu syntaxe všech šablon můžete použít nástroj Linter, který se spouští z konzole:

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

Parametrem `--strict` aktivujete [striktní režim|#striktní režim].

Pokud používáte vlastní značky, vytvořte si vlastní verzi Linteru, např. `custom-latte-lint`:

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

require __DIR__ . '/vendor/autoload.php';

$path = $argv[1] ?? '.';

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// zde přidejte svá rozšíření
$latte->addExtension(/* ... */);

$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);
```

Alternativně můžete předat vlastní objekt `Latte\Engine` do Linteru:

```php
$latte = new Latte\Engine;
// zde nakonfigurujeme objekt $latte
$linter = new Latte\Tools\Linter(engine: $latte);
```


Načítání šablon z řetězce
=========================

Pro načítání šablon z řetězců, například pro účely testování, můžete použít [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);
```


Exception handler
=================

Můžete si definovat vlastní obslužný handler pro očekávané výjimky. Předají se mu výjimky vzniklé uvnitř [`{try}`|tags#try] a v [sandboxu|sandbox].

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

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


Automatické dohledávání layoutu
===============================

Pomocí značky [`{layout}`|template-inheritance#layoutova-dedicnost] šablona určuje svou rodičovskou šablonu. Je taky možné nechat dohledávat layout automaticky, což zjednoduší psaní šablon, neboť v nich nebude nutné značku `{layout}` uvádět.

Dosáhne se toho následujícím způsobem:

```php
$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// vrací cestu k souboru s layoutem
		return 'automatic.layout.latte';
	}
};

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

Pokud šablona nemá mít layout, použije se značka `{layout none}`.

Vývojářské postupy

Instalace

Nejlepší způsob instalace Latte je pomocí Composeru:

composer require latte/latte

Podporované verze PHP (platí pro poslední setinkové verze Latte):

verze kompatibilní s PHP
Latte 3.0 PHP 8.0 – 8.2

Vykreslení šablony

Jak vykreslit šablonu? Stačí k tomu tento jednoduchý kód:

$latte = new Latte\Engine;
// nastavení adresáře pro cache
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* proměnné šablony */ ];
// nebo $params = new TemplateParameters(/* ... */);

// vykreslení na výstup
$latte->render('template.latte', $params);
// vykreslení do proměnné
$output = $latte->renderToString('template.latte', $params);

Parametry mohou být pole nebo ještě lépe objekt, který zajistí typovou kontrolu a napovídání v editorech.

Ukázky použití najdete také v repozitáři Latte examples.

Výkon a cache

Šablony v Latte jsou extrémně rychlé, protože se kompilují přímo do PHP kódu a ukládají do cache na disk. Nemají tedy žádnou režii navíc oproti šablonám psaným v čistém PHP.

Cache se automaticky regeneruje při změně zdrojového souboru. Během vývoje tedy pohodlně editujete šablony v Latte a změny okamžitě vidíte v prohlížeči. Pro mírné zvýšení výkonu v produkčním prostředí můžete automatickou regeneraci vypnout:

$latte->setAutoRefresh(false);

Při nasazení na produkční server může prvotní vygenerování cache, zejména u rozsáhlých aplikací, chvíli trvat. Latte má vestavěnou prevenci proti cache stampede, což je situace, kdy velký počet souběžných požadavků spustí generování cache současně, což by mohlo přetížit server. Latte tento problém řeší chytře – při více souběžných požadavcích generuje cache pouze první vlákno, zatímco ostatní čekají a následně využijí vytvořenou cache.

Parametry jako třída

Místo předávání proměnných do šablony jako pole je lepší vytvořit si třídu. Získáte tak typově bezpečný zápis, příjemné napovídání v IDE a možnost registrace filtrů a funkcí.

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

Vypnutí auto-escapování proměnné

Pokud proměnná obsahuje HTML řetězec, můžete ji označit tak, aby ji Latte automaticky (a tedy dvojitě) neescapovalo. Tím se vyhnete potřebě uvádět v šabloně |noescape.

Nejjednodušší způsob je zabalit řetězec do objektu Latte\Runtime\Html:

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

Latte také neescapuje objekty implementující rozhraní Latte\HtmlStringable. Můžete si vytvořit vlastní třídu, jejíž metoda __toString() bude vracet HTML kód, který se nebude automaticky escapovat:

class Emphasis implements Latte\HtmlStringable
{
	public function __construct(
		private string $str,
	) {
	}

	public function __toString(): string
	{
		return '<em>' . htmlspecialchars($this->str) . '</em>';
	}
}

$params = [
	'foo' => new Emphasis('hello'),
];

Metoda __toString musí vracet korektní HTML a zajistit escapování parametrů, jinak může dojít ke zranitelnosti XSS!

Jak rozšířit Latte o filtry, značky atd.

Informace o přidávání vlastních filtrů, funkcí, značek atd. najdete v kapitole rozšiřujeme Latte. Pokud chcete své úpravy znovu použít v různých projektech nebo je sdílet s ostatními, měli byste vytvořit rozšíření.

Libovolný PHP kód v šabloně {php ...}

Zatímco uvnitř značky {do} lze zapisovat pouze PHP výrazy, rozšíření RawPhpExtension přidává značku {php ...}, která umožňuje vkládat jakýkoliv PHP kód. Použití je na zodpovědnost autora šablony, protože se na ni nevztahují pravidla sandbox režimu.

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

Kontrola vygenerovaného kódu

Latte kompiluje šablony do PHP kódu a dbá na jeho syntaktickou správnost. Nicméně při použití rozšíření třetích stran nebo RawPhpExtension nemůže Latte zaručit korektnost vygenerovaného souboru. V PHP lze také zapsat kód, který je syntakticky správný, ale zakázaný (například přiřazení hodnoty do $this) a způsobí PHP Compile Error. Pokud takovou operaci zapíšete v šabloně, dostane se i do vygenerovaného PHP kódu. Jelikož v PHP existují na dvě stovky různých zakázaných operací, nemá Latte ambici je odhalovat. Upozorní na ně až samotné PHP při vykreslení, což obvykle ničemu nevadí.

Pokud chcete odhalit tyto problémy už v době kompilace šablony, zejména pokud šablony mohou editovat uživatelé nebo používáte Sandbox, můžete zapnout kontrolu pomocí Engine::enablePhpLint(). Ke kontrole je potřeba volat binárku PHP, jejíž cestu předáte jako parametr:

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

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// zachytí chyby v Latte a také Compile Error v PHP
	echo 'Error: ' . $e->getMessage();
}

Národní prostředí

Latte umožňuje nastavit národní prostředí, které ovlivňuje formátování čísel, datumů a řazení. Nastavuje se pomocí metody setLocale(). Identifikátor prostředí se řídí standardem IETF language tag, který používá rozšíření PHP intl. Skládá se z kódu jazyka a případně kódu země, např. en_US pro angličtinu ve Spojených státech, de_DE pro němčinu v Německu atd.

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

Nastavení prostředí ovlivňuje filtry localDate, sort, number a bytes.

Vyžaduje PHP rozšíření intl. Nastavení v Latte neovlivňuje globální nastavení locale v PHP.

Striktní režim

Ve striktním režimu parsování Latte kontroluje, zda nechybí uzavírací HTML značky a zakazuje používání proměnné $this. Zapnete jej takto:

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

Generování šablon s hlavičkou declare(strict_types=1) zapnete takto:

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

Překládání v šablonách

Rozšíření TranslatorExtension přidává do šablony značky {_...}, {translate} a filtr translate pro překládání hodnot nebo částí šablony do jiných jazyků. Jako parametr uvedeme metodu provádějící překlad:

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

	public function translate(string $original): string
	{
		// z $original vytvoříme $translated dle $this->lang
		return $translated;
	}
}

$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...), // [$translator, 'translate'] v PHP 8.0
);
$latte->addExtension($extension);

Translator se volá za běhu při vykreslování šablony. Latte ovšem umí všechny statické texty překládat už během kompilace šablony. Tím se ušetří výkon, protože každý řetězec se přeloží jen jednou a výsledný překlad se zapíše do zkompilované podoby. V adresáři s cache tak vznikne více zkompilovaných verzí šablony, jedna pro každý jazyk. K tomu stačí pouze uvést jazyk jako druhý parametr:

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

Statickým textem je myšleno třeba {_'hello'} nebo {translate}hello{/translate}. Nestatické texty, jako třeba {_$foo}, se nadále budou překládat za běhu.

Překladači lze ze šablony předávat i doplňující parametry pomocí {_$original, foo: bar} nebo {translate foo: bar}, které získá jako pole $params:

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

Debuggování a Tracy

Latte se vám snaží vývoj co nejvíce zpříjemnit. Nabízí několik nástrojů pro debugování, včetně značek {dump}, {debugbreak} a {trace}.

Největší komfort získáte, když ještě si nainstalujete skvělý ladicí nástroj Tracy a aktivujete doplněk pro Latte:

// zapne Tracy
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// aktivuje rozšíření pro Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

Nyní se vám budou všechny chyby zobrazovat v přehledné červené obrazovce, včetně chyb v šablonách se zvýrazněním řádku a sloupce (video). Zároveň v pravém dolním rohu v tzv. Tracy Baru se objeví záložka pro Latte, kde jsou přehledně vidět všechny vykreslované šablony a jejich vzájemné vztahy (včetně možnosti se do šablony nebo zkompilovaného kódu prokliknout) a také proměnné:

Jelikož Latte kompiluje šablony do přehledného PHP kódu, můžete je pohodlně krokovat ve svém IDE.

Linter: validace syntaxe šablon

Pro kontrolu syntaxe všech šablon můžete použít nástroj Linter, který se spouští z konzole:

vendor/bin/latte-lint <cesta>

Parametrem --strict aktivujete striktní režim.

Pokud používáte vlastní značky, vytvořte si vlastní verzi Linteru, např. custom-latte-lint:

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

require __DIR__ . '/vendor/autoload.php';

$path = $argv[1] ?? '.';

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// zde přidejte svá rozšíření
$latte->addExtension(/* ... */);

$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);

Alternativně můžete předat vlastní objekt Latte\Engine do Linteru:

$latte = new Latte\Engine;
// zde nakonfigurujeme objekt $latte
$linter = new Latte\Tools\Linter(engine: $latte);

Načítání šablon z řetězce

Pro načítání šablon z řetězců, například pro účely testování, můžete použít StringLoader:

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

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

Exception handler

Můžete si definovat vlastní obslužný handler pro očekávané výjimky. Předají se mu výjimky vzniklé uvnitř {try} a v sandboxu.

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

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

Automatické dohledávání layoutu

Pomocí značky {layout} šablona určuje svou rodičovskou šablonu. Je taky možné nechat dohledávat layout automaticky, což zjednoduší psaní šablon, neboť v nich nebude nutné značku {layout} uvádět.

Dosáhne se toho následujícím způsobem:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// vrací cestu k souboru s layoutem
		return 'automatic.layout.latte';
	}
};

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

Pokud šablona nemá mít layout, použije se značka {layout none}.