Nette Documentation Preview

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


Instalace
=========

Nejlepší způsob, jak nainstalovat 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
| Latte 2.8 – 2.11| PHP 7.1 – 8.1


Jak vykreslit šablonu
=====================

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

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

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

// kresli na výstup
$latte->render('template.latte', $params);
// kresli 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 nesmírně rychlé, Latte je totiž kompiluje přímo do PHP kódu a ukládá do cache na disk. Nemají tedy žádnou režii navíc oproti šablonám psaným v čistém PHP.

Cache se automaticky regeneruje pokaždé, když změníte zdrojový soubor. Během vývoje si tedy pohodlně editujete šablony v Latte a změny okamžitě vidíte v prohlížeči. Tuto funkci můžete v produkčním prostředí vypnout a ušetřit tím malinko výkonu:

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

Při nasazení na produkčním serveru může prvotní vygenerování cache, zejména u rozsáhlejších aplikací, pochopitelně chviličku trvat. Latte má vestavěnou prevenci před "cache stampede":https://en.wikipedia.org/wiki/Cache_stampede.
Jde o situaci, kdy se sejde větší počet souběžných požadavků, které spustí Latte, a protože cache ještě neexistuje, začaly by ji všechny generovat současně. Což by neúměrně zatížilo server.
Latte je chytré a při více souběžných požadavcích generuje cache pouze první vlákno, ostatní čekají a následně ji využíjí.


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

Lepší než předávat proměnné do šablony jako pole je 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 cestu pro [registraci 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 řetězec v HTML, můžete ji označit tak, aby ji Latte automaticky (a tedy dvojitě) neescapovalo. Vyhnete se tak potřebě uvádět v šabloně `|noescape`.

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

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

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

```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]
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.
========================================

Jak do Latte přidat vlastní filtr, funkci, značku atd? O tom pojednává kapitola [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ý kód v šabloně `{php ...}` .{data-version:3.0}{toc: RawPhpExtension}
=============================================================================

Uvnitř značky [`{do}`|tags#do] lze zapisovat pouze PHP výrazy, nemůžete tak třeba vložit konstrukce jako `if ... else` nebo statementy ukončené středníkem.

Můžete si však zaregistrovat rozšíření `RawPhpExtension`, které přidá značku `{php ...}`, kterou lze vkládat jakýkoliv PHP kód na zodpovědnost autora šablony.

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


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

Pomocí rozšíření `TranslatorExtension` přidáte do šablony značky [`{_...}`|tags#_], [`{translate}`|tags#translate] a filtr [`translate`|filters#translate]. Slouží k překládání hodnot nebo částí šablony do jiných jazyků. Jako parametr uvedeme metodu (PHP callable) 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. Přímo pro účely debugování existuje trojice 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();
// aktivuje rozšíření pro Tracy "bluescreen"
Latte\Bridges\Tracy\BlueScreenPanel::initialize();

// přidá panel do Tracy Bar pro konkrétní instanci Latte Engine
$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);
```

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ě ve svém IDE krokovat.


Linter: validace syntaxe šablon .{data-version:2.11}{toc: Linter}
=================================================================

Projít všechny šablony a zkontrolovat, zda neobsahují syntaktické chyby, vám pomůže nástroj Linter. Spouští se z konzole:

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

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

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

// zadejte skutečnou cestu k soubor autoload.php
require __DIR__ . '/vendor/autoload.php';

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

$engine = new Latte\Engine;
// tady zaregistruje jednotlivá rozšíření
$engine->addExtension(/* ... */);

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


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

Potřebujete načítat šablony z řetězců místo souborů, třeba pro účely testování? Pomůže vám [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, oznámí to značkou `{layout none}`.


{{composer: latte/latte}}

Vývojářské postupy

Instalace

Nejlepší způsob, jak nainstalovat 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
Latte 2.8 – 2.11 PHP 7.1 – 8.1

Jak vykreslit šablonu

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

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

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

// kresli na výstup
$latte->render('template.latte', $params);
// kresli 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 nesmírně rychlé, Latte je totiž kompiluje přímo do PHP kódu a ukládá do cache na disk. Nemají tedy žádnou režii navíc oproti šablonám psaným v čistém PHP.

Cache se automaticky regeneruje pokaždé, když změníte zdrojový soubor. Během vývoje si tedy pohodlně editujete šablony v Latte a změny okamžitě vidíte v prohlížeči. Tuto funkci můžete v produkčním prostředí vypnout a ušetřit tím malinko výkonu:

$latte->setAutoRefresh(false);

Při nasazení na produkčním serveru může prvotní vygenerování cache, zejména u rozsáhlejších aplikací, pochopitelně chviličku trvat. Latte má vestavěnou prevenci před cache stampede. Jde o situaci, kdy se sejde větší počet souběžných požadavků, které spustí Latte, a protože cache ještě neexistuje, začaly by ji všechny generovat současně. Což by neúměrně zatížilo server. Latte je chytré a při více souběžných požadavcích generuje cache pouze první vlákno, ostatní čekají a následně ji využíjí.

Parametry jako třída

Lepší než předávat proměnné do šablony jako pole je vytvořit si třídu. Získáte tak typově bezpečný zápis, příjemné napovídání v IDE a cestu pro registraci 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 řetězec v HTML, můžete ji označit tak, aby ji Latte automaticky (a tedy dvojitě) neescapovalo. Vyhnete se tak potřebě uvádět v šabloně |noescape.

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

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

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

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

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.

Jak do Latte přidat vlastní filtr, funkci, značku atd? O tom pojednává kapitola 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ý kód v šabloně {php ...}

Uvnitř značky {do} lze zapisovat pouze PHP výrazy, nemůžete tak třeba vložit konstrukce jako if ... else nebo statementy ukončené středníkem.

Můžete si však zaregistrovat rozšíření RawPhpExtension, které přidá značku {php ...}, kterou lze vkládat jakýkoliv PHP kód na zodpovědnost autora šablony.

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

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

Pomocí rozšíření TranslatorExtension přidáte do šablony značky {_...}, {translate} a filtr translate. Slouží k překládání hodnot nebo částí šablony do jiných jazyků. Jako parametr uvedeme metodu (PHP callable) 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. Přímo pro účely debugování existuje trojice 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();
// aktivuje rozšíření pro Tracy "bluescreen"
Latte\Bridges\Tracy\BlueScreenPanel::initialize();

// přidá panel do Tracy Bar pro konkrétní instanci Latte Engine
$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);

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ě ve svém IDE krokovat.

Linter: validace syntaxe šablon

Projít všechny šablony a zkontrolovat, zda neobsahují syntaktické chyby, vám pomůže nástroj Linter. Spouští se z konzole:

vendor/bin/latte-lint <cesta>

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

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

// zadejte skutečnou cestu k soubor autoload.php
require __DIR__ . '/vendor/autoload.php';

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

$engine = new Latte\Engine;
// tady zaregistruje jednotlivá rozšíření
$engine->addExtension(/* ... */);

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

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

Potřebujete načítat šablony z řetězců místo souborů, třeba pro účely testování? Pomůže vám 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, oznámí to značkou {layout none}.