Latte pro vývojáře

Jak vykreslit šablonu

Jak vykreslit šablonu? Stačí spustit tento kód:

$latte = new Latte\Engine;

$latte->setTempDirectory('/path/to/tempdir');

$params = [
	'items' => ['one', 'two', 'three'],
];

// kresli na výstup
$latte->render('template.latte', $params);
// kresli do řetězce
$html = $latte->renderToString('template.latte', $params);

Latte automaticky přegenerovává cache při každé změně šablony, což můžeme v produkčním prostředí vypnout a ušetřit tím malinko výkonu:

$latte->setAutoRefresh(false);

Typově bezpečné parametry

Místo pole $params můžete také použít objekt, což přináší některé výhody. Získáte typově bezpečný zápis, příjemné napovídání v IDE a cestu pro registraci filtrů a funkcí. Příklad zápisu v PHP 8:

class MailTemplate
{
	public function __construct(
		public string $lang = 'cs',
		public Address $address,
		public string $subject,
		public array $items,
		public ?float $price = null,
	) {}
}

$latte->render('mail.latte', new MailTemplate(
	lang: $this->lang,
	subject: $title,
	price: $this->getPrice(),
	items: [],
	address: $userAddress,
));

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

Pokud nemáme naši šablonu uloženou v souboru, ale pouze v proměnných, musíme nastavit loader na Latte\Loaders\StringLoader.

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

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

Vlastní filtr

Jako filtr lze do šablony zaregistrovat libovolný callback:

$latte = new Latte\Engine;
$latte->addFilter('shortify', function (string $s): string {
	return mb_substr($s, 0, 10); // zkrátí text na 10 písmen
});

V tomto případě by bylo šikovnější, kdyby filtr přijímal další parametr:

$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

V šabloně se potom volá takto:

<p>{$text|shortify}</p>
<p>{$text|shortify:100}</p>

Druhým způsobem definice filtru je třída šablony. Důležité je uvést anotaci @filter:

class MyTemplate
{
	/** @filter */
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len); // zkrátí text na 10 písmen
	}
}

$params = new MyTemplate;
...
$latte->render('template.latte', $params);

V PHP 8.0 můžete místo anotace použít tzv. atribut:

#[Latte\Attributes\TemplateFilter]
public function shortify(string $s, int $len = 10): string
{
	return mb_substr($s, 0, $len);
}

Zavaděč filtrů

Ruční registraci více filtrů lze nahradit jedním zavaděčem filtrů:

$latte->addFilterLoader([new Filters, 'loader']);

Metoda loader() získá název filtru a vrátí callable:

class Filters
{
	public function loader(string $filter): ?callable
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter];
		}
		return null;
	}

	public function shortify($s, $len = 10)
	{
		return mb_substr($s, 0, $len);
	}

	...
}

Universal filter

Před Latte v2.10 se místo zavaděče filtrů používal tento způsob:

$latte->addFilter(null, [new Filters, 'loader']);

class Filters
{
	public function loader(string $filter, ...$args)
	{
		if (method_exists($this, $filter)) {
			return [$this, $filter](...$args);
		}
	}

	...
}

Escapování

Řetězec, který filtr přijímá na vstupu a vrací na výstupu, je standardně plain text v UTF‑8. V případě, že filtr vrací HTML, zabalí řetězec do objektu Latte\Runtime\Html, aby nedošlo k nežádoucímu automatickému escapování v šabloně:

$latte->addFilter('money', function (int $num): string {
	return new Latte\Runtime\Html("<i>$num Kč</i");
});

Filtr musí zajistit správné escapování dat.

Blokové filtry

Filtry, které se používají na bloky (např. anonymní bloky) dostávají informaci o typu obsahu a kontextu v objektu Latte\Runtime\FilterInfo, který se předává jako první parameter. Filtr by měl nejprve ověřit, že daný content type podporuje:

use Latte\Engine;
use Latte\Runtime\FilterInfo;

$latte->addFilter('tidyHtml', function (FilterInfo $info, string $s): string {
	// ověříme, že v $s je HTML text
	if ($info->contentType !== Engine::CONTENT_HTML) {
		throw new Exception('Filter |tidyHtml used in incompatible content type.');
	}
	...
});

U těch filtrů se nepoužívá zabalení do objektu Latte\Runtime\Html, informace o typu se vrací v objektu $info. Tj. pokud vrací data v jiném typu, změní hodnotu v $info:

$info->contentType = Engine::CONTENT_TEXT;

Rozlišují se následující typy a jejich kontexty:

use Latte\Engine;
use Latte\Compiler;

Engine::CONTENT_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TEXT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_TAG
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_ATTRIBUTE
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_COMMENT
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_CSS
Engine::CONTENT_HTML . Compiler::CONTEXT_HTML_JS
Engine::CONTENT_XHTML
Engine::CONTENT_XML
Engine::CONTENT_JS
Engine::CONTENT_CSS
Engine::CONTENT_ICAL

Pokud se blokový filtr použije běžným způsobem (tj. {$foo|tidyHtml}), obsahuje $info->contentType hodnotu null.

Funkce

V Latte můžete používat všechny funkce PHP a zároveň si definovat své vlastní:

$latte = new Latte\Engine;
$latte->addFunction('random', function (...$args) {
	return $args[array_rand($args)];
});

Použití je pak stejné, jako když voláte PHP funkci:

{random(jablko, pomeranč, citron)} // vypíše například: jablko

Druhým způsobem definice funkce je třída šablony. Důležité je uvést anotaci @function:

class MyTemplate
{
	/** @function */
	public function random(...$args)
	{
		return $args[array_rand($args)];
	}
}

$params = new MyTemplate;
...
$latte->render('template.latte', $params);

V PHP 8.0 můžete místo anotace použít tzv. atribut:

#[Latte\Attributes\TemplateFunction]
public function random(...$args)
{
	return $args[array_rand($args)];
}

Vlastní tagy

Latte poskytuje API pro tvorbu vlastních tagů. Není to nic složitého. Značky přidáváme v sadách, přičemž sadu může tvořit i jediná značka.

$latte = new Latte\Engine;

// vytvoříme si sadu
$set = new Latte\Macros\MacroSet($latte->getCompiler());

// do sady přidáme párové tagy {try} ... {/try}
$set->addMacro(
	'try', // název tagu
	'try {',  // PHP kód nahrazující otevírací tag
	'} catch (\Exception $e) {}' // kód nahrazující uzavírací tag
);

Pokud značka není párová, třetí parametr metody addMacro() vynecháme.

PHP kód uváděný ve druhém a třetím parametru může obsahovat zástupné symboly:

  • %node.word – vloží první argument tagu
  • %node.array – vloží argumenty tagu naformátované jako PHP pole
  • %node.args – vloží argumenty tagu naformátované jako PHP kód
  • %node.line – vloží komentář s číslem řádku v šabloně
  • %escape(...) – nahradí za aktuální escapovací funkci
  • %modify(...) – nahradí sérií filtrovacích funkcí

Příklad:

$set->addMacro('if', 'if (%node.args):', 'endif');

Pokud je logika značek ještě složitější, můžeme místo řetězců uvést callbacky či lambda funkce. Jako první parametr dostanou objekt MacroNode reprezentující aktuální uzel, druhým parametrem je objekt PhpWriter, který usnadňuje generování výstupního kódu.

$set->addMacro('if', function ($node, $writer) {
	return $writer->write('if (%node.args):');
}, 'endif');

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

Dohledávání layoutu

Rozhraní API můžete použít k výběru šablony layout, která se má použít, když podřízená šablona neobsahuje značku {layout}. To zjednoduší psaní šablon nebo umožní automatické dohledávání layoutů.

Toho se dosáhne 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);