Nette Documentation Preview

syntax
Practices for Developers
************************


Installation
============

The best way how to install Latte is to use a Composer:

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

Supported PHP versions (applies to the latest patch Latte versions):

| version         | compatible with PHP
|-----------------|-------------------
| Latte 3.0       | PHP 8.0 – 8.2
| Latte 2.8 – 2.11| PHP 7.1 – 8.1


How to Render a Template
========================

How to render a template? Just use this simple code:

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

$params = [ /* template variables */ ];
// or $params = new TemplateParameters(/* ... */);

// render to output
$latte->render('template.latte', $params);
// or render to variable
$output = $latte->renderToString('template.latte', $params);
```

Parameters can be arrays or even better [object|#Parameters as a class], which will provide type checking and suggestion in the editor.

.[note]
You can also find usage examples in the repository [Latte examples |https://github.com/nette-examples/latte].


Performance and Caching
=======================

Latte templates are extremely fast, because Latte compiles them directly into PHP code and caches them on disk. Thus, they have no extra overhead compared to templates written in pure PHP.

The cache is automatically regenerated every time you change the source file. So you can conveniently edit your Latte templates during development and see the changes immediately in the browser. You can disable this feature in a production environment and save a little performance:

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

When deployed on a production server, the initial cache generation, especially for larger applications, can understandably take a while. Latte has built-in prevention against "cache stampede":https://en.wikipedia.org/wiki/Cache_stampede.
This is a situation where server receives a large number of concurrent requests and because Latte's cache does not yet exist, they would all generate it at the same time. Which spikes CPU.
Latte is smart, and when there are multiple concurrent requests, only the first thread generates the cache, the others wait and then use it.


Parameters as a Class
=====================

Better than passing variables to the template as arrays is to create a class. You get [type-safe notation|type-system], [nice suggestion in IDE|/recipes#Editors and IDE]
and a way to [register filters|extending-latte#Filters Using the Class] and [functions|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,
));
```


Disabling Auto-Escaping of Variable
===================================

If the variable contains an HTML string, you can mark it so that Latte does not automatically (and therefore double) escape it. This avoids the need to specify `|noescape` in the template.

The easiest way is to wrap the string in a `Latte\Runtime\Html` object:

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

Latte also does not escape all objects that implement the `Latte\HtmlStringable` interface. So you can create your own class whose `__toString()` method will return HTML code that will not be escaped automatically:

```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]
The `__toString` method must return correct HTML and provide parameter escaping, otherwise an XSS vulnerability may occur!


How to Extend Latte with Filters, Tags, etc.
============================================

How to add a custom filter, function, tag, etc. to Latte? Find out in the chapter [extending Latte].
If you want to reuse your changes in different projects or if you want to share them with others, you should then [create an extension |creating-extension].


Any Code in Template `{php ...}` .{data-version:3.0}{toc: RawPhpExtension}
==========================================================================

Only PHP expressions can be written inside the [`{do}`|tags#do] tag, so you can't, for example, insert constructs like `if ... else` or semicolon-terminated statements.

However, you can register the `RawPhpExtension` extension, which adds the `{php ...}` tag, which can be used to insert any PHP code at the template author's risk.

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


Translation in Templates .{data-version:3.0}{toc: TranslatorExtension}
======================================================================

Use the `TranslatorExtension` extension to add [`{_...}`|tags#_], [`{translate}`|tags#translate] and filter [`translate`|filters#translate] to the template. They are used to translate values or parts of the template into other languages. The parameter is the method (PHP callable) that performs the translation:

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

	public function translate(string $original): string
	{
		// create $translated from $original according to $this->lang
		return $translated;
	}
}

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

The translator is called at runtime when the template is rendered. However, Latte can translate all static texts during template compilation. This saves performance because each string is translated only once and the resulting translation is written to the compiled file. This creates multiple compiled versions of the template in the cache directory, one for each language. To do this, you only need to specify the language as the second parameter:

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

By static text we mean, for example, `{_'hello'}` or `{translate}hello{/translate}`. Non-static text, such as `{_$foo}`, will continue to be translated at runtime.

The template can also pass additional parameters to the translator via `{_$original, foo: bar}` or `{translate foo: bar}`, which it receives as the `$params` array:

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


Debugging and Tracy
===================

Latte tries to make the development as pleasant as possible. For debugging purposes, there are three tags [`{dump}`|tags#dump], [`{debugbreak}`|tags#debugbreak] and [`{trace}`|tags#trace].

You'll get the most comfort if you install the great [debugging tool Tracy|tracy:] and activate the Latte plugin:

```php
// enables Tracy
Tracy\Debugger::enable();
// activates Tracy's "bluescreen" extension.
Latte\Bridges\Tracy\BlueScreenPanel::initialize();

// adds a panel to the Tracy Bar for a specific Latte Engine instance
$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);
```

You will now see all errors in a neat red screen, including errors in templates with row and column highlighting ([video|https://github.com/nette/tracy/releases/tag/v2.9.0]).
At the same time, in the bottom right corner in the so-called Tracy Bar, a tab for Latte appears, where you can clearly see all rendered templates and their relationships (including the possibility to click into the template or compiled code), as well as variables:

[* latte-debugging.webp *]

Since Latte compiles templates into readable PHP code, you can conveniently step through them in your IDE.


Linter: Validating the Template Syntax .{data-version:2.11}{toc: Linter}
========================================================================

The Linter tool will help you go through all templates and check for syntax errors. It is launched from the console:

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

If you use custom tags, also create your customized Linter, e.g. `custom-latte-lint`:

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

// enter the actual path to the autoload.php file
require __DIR__ . '/vendor/autoload.php';

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

$engine = new Latte\Engine;
// registers individual extensions here
$engine->addExtension(/* ... */);

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


Loading Templates from a String
===============================

Need to load templates from strings instead of files, perhaps for testing purposes? [StringLoader|extending-latte#stringloader] will help you:

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

You can define your own handler for expected exceptions. Exceptions raised inside [`{try}`|tags#try] and in the [sandbox] are passed to it.

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

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


Automatic Layout Lookup
=======================

Using the tag [`{layout}`|template-inheritance#layout-inheritance], the template determines its parent template. It's also possible to have the layout searched automatically, which will simplify writing templates since they won't need to include the `{layout}` tag.

This is achieved as follows:

```php
$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// it returns the path to the parent template file
		return 'automatic.layout.latte';
	}
};

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

If the template should not have a layout, it will indicate this with the `{layout none}` tag.


{{composer: latte/latte}}

Practices for Developers

Installation

The best way how to install Latte is to use a Composer:

composer require latte/latte

Supported PHP versions (applies to the latest patch Latte versions):

version compatible with PHP
Latte 3.0 PHP 8.0 – 8.2
Latte 2.8 – 2.11 PHP 7.1 – 8.1

How to Render a Template

How to render a template? Just use this simple code:

$latte = new Latte\Engine;
// cache directory
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* template variables */ ];
// or $params = new TemplateParameters(/* ... */);

// render to output
$latte->render('template.latte', $params);
// or render to variable
$output = $latte->renderToString('template.latte', $params);

Parameters can be arrays or even better object, which will provide type checking and suggestion in the editor.

You can also find usage examples in the repository Latte examples.

Performance and Caching

Latte templates are extremely fast, because Latte compiles them directly into PHP code and caches them on disk. Thus, they have no extra overhead compared to templates written in pure PHP.

The cache is automatically regenerated every time you change the source file. So you can conveniently edit your Latte templates during development and see the changes immediately in the browser. You can disable this feature in a production environment and save a little performance:

$latte->setAutoRefresh(false);

When deployed on a production server, the initial cache generation, especially for larger applications, can understandably take a while. Latte has built-in prevention against cache stampede. This is a situation where server receives a large number of concurrent requests and because Latte's cache does not yet exist, they would all generate it at the same time. Which spikes CPU. Latte is smart, and when there are multiple concurrent requests, only the first thread generates the cache, the others wait and then use it.

Parameters as a Class

Better than passing variables to the template as arrays is to create a class. You get type-safe notation, nice suggestion in IDE and a way to register filters and functions.

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

Disabling Auto-Escaping of Variable

If the variable contains an HTML string, you can mark it so that Latte does not automatically (and therefore double) escape it. This avoids the need to specify |noescape in the template.

The easiest way is to wrap the string in a Latte\Runtime\Html object:

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

Latte also does not escape all objects that implement the Latte\HtmlStringable interface. So you can create your own class whose __toString() method will return HTML code that will not be escaped automatically:

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

The __toString method must return correct HTML and provide parameter escaping, otherwise an XSS vulnerability may occur!

How to Extend Latte with Filters, Tags, etc.

How to add a custom filter, function, tag, etc. to Latte? Find out in the chapter extending Latte. If you want to reuse your changes in different projects or if you want to share them with others, you should then create an extension.

Any Code in Template {php ...}

Only PHP expressions can be written inside the {do} tag, so you can't, for example, insert constructs like if ... else or semicolon-terminated statements.

However, you can register the RawPhpExtension extension, which adds the {php ...} tag, which can be used to insert any PHP code at the template author's risk.

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

Translation in Templates

Use the TranslatorExtension extension to add {_...}, {translate} and filter translate to the template. They are used to translate values or parts of the template into other languages. The parameter is the method (PHP callable) that performs the translation:

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

	public function translate(string $original): string
	{
		// create $translated from $original according to $this->lang
		return $translated;
	}
}

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

The translator is called at runtime when the template is rendered. However, Latte can translate all static texts during template compilation. This saves performance because each string is translated only once and the resulting translation is written to the compiled file. This creates multiple compiled versions of the template in the cache directory, one for each language. To do this, you only need to specify the language as the second parameter:

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

By static text we mean, for example, {_'hello'} or {translate}hello{/translate}. Non-static text, such as {_$foo}, will continue to be translated at runtime.

The template can also pass additional parameters to the translator via {_$original, foo: bar} or {translate foo: bar}, which it receives as the $params array:

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

Debugging and Tracy

Latte tries to make the development as pleasant as possible. For debugging purposes, there are three tags {dump}, {debugbreak} and {trace}.

You'll get the most comfort if you install the great debugging tool Tracy and activate the Latte plugin:

// enables Tracy
Tracy\Debugger::enable();
// activates Tracy's "bluescreen" extension.
Latte\Bridges\Tracy\BlueScreenPanel::initialize();

// adds a panel to the Tracy Bar for a specific Latte Engine instance
$latte = new Latte\Engine;
Latte\Bridges\Tracy\LattePanel::initialize($latte);

You will now see all errors in a neat red screen, including errors in templates with row and column highlighting (video). At the same time, in the bottom right corner in the so-called Tracy Bar, a tab for Latte appears, where you can clearly see all rendered templates and their relationships (including the possibility to click into the template or compiled code), as well as variables:

Since Latte compiles templates into readable PHP code, you can conveniently step through them in your IDE.

Linter: Validating the Template Syntax

The Linter tool will help you go through all templates and check for syntax errors. It is launched from the console:

vendor/bin/latte-lint <path>

If you use custom tags, also create your customized Linter, e.g. custom-latte-lint:

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

// enter the actual path to the autoload.php file
require __DIR__ . '/vendor/autoload.php';

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

$engine = new Latte\Engine;
// registers individual extensions here
$engine->addExtension(/* ... */);

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

Loading Templates from a String

Need to load templates from strings instead of files, perhaps for testing purposes? StringLoader will help you:

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

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

Exception Handler

You can define your own handler for expected exceptions. Exceptions raised inside {try} and in the sandbox are passed to it.

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

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

Automatic Layout Lookup

Using the tag {layout}, the template determines its parent template. It's also possible to have the layout searched automatically, which will simplify writing templates since they won't need to include the {layout} tag.

This is achieved as follows:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// it returns the path to the parent template file
		return 'automatic.layout.latte';
	}
};

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

If the template should not have a layout, it will indicate this with the {layout none} tag.