Nette Documentation Preview

syntax
Práticas para os desenvolvedores
********************************


Instalação .[#toc-installation]
===============================

A melhor maneira de instalar o Latte é usar um Composer:

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

Versões PHP suportadas (aplica-se às últimas versões de correção Latte):

| versão | compatível com PHP
|-----------------|-------------------
PHP 3.0 | Latte 3.0 | PHP 8.0 - 8.2


Como fazer um modelo .[#toc-how-to-render-a-template]
=====================================================

Como fazer um modelo? Basta usar este código simples:

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

$params = [ /* template variables */ ];
// ou $params = novos parâmetros TemplateParameters(/* ... */);

// render à saída
$latte->render('template.latte', $params);
// ou render a variável
$output = $latte->renderToString('template.latte', $params);
```

Os parâmetros podem ser arrays ou até melhor [objeto |#Parameters as a class], o que proporcionará verificação de tipo e sugestão no editor.

.[note]
Você também pode encontrar exemplos de uso no repositório de [exemplos Latte |https://github.com/nette-examples/latte].


Desempenho e Caching .[#toc-performance-and-caching]
====================================================

Os modelos Latte são extremamente rápidos, porque o Latte os compila diretamente em código PHP e os armazena em cache em disco. Assim, eles não têm despesas extras em comparação com os modelos escritos em PHP puro.

O cache é automaticamente regenerado cada vez que você muda o arquivo fonte. Assim, você pode editar convenientemente seus modelos Latte durante o desenvolvimento e ver as mudanças imediatamente no navegador. Você pode desativar este recurso em um ambiente de produção e economizar um pouco de desempenho:

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

Quando implantada em um servidor de produção, a geração inicial de cache, especialmente para aplicações maiores, pode, compreensivelmente, demorar um pouco. O Latte tem prevenção embutida contra  a "debandada do cache:https://en.wikipedia.org/wiki/Cache_stampede".
Esta é uma situação em que o servidor recebe um grande número de solicitações simultâneas e como o cache do Latte ainda não existe, todos eles o gerariam ao mesmo tempo. O que aumenta a CPU.
O Latte é inteligente, e quando há várias solicitações simultâneas, apenas o primeiro tópico gera o cache, os outros esperam e depois o utilizam.


Parâmetros como classe .[#toc-parameters-as-a-class]
====================================================

Melhor do que passar variáveis para o modelo como matrizes é criar uma classe. Você recebe [notação de segurança de tipo |type-system], [boa sugestão no IDE |recipes#Editors and IDE] e uma forma de [registrar filtros |extending-latte#Filters Using the Class] e [funções |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,
));
```


Desativação do Auto-Escaping da variável .[#toc-disabling-auto-escaping-of-variable]
====================================================================================

Se a variável contém uma string HTML, você pode marcá-la para que Latte não fuja automaticamente (e portanto duplique) dela. Isto evita a necessidade de especificar `|noescape` no modelo.

A maneira mais fácil é enrolar o cordel em um objeto `Latte\Runtime\Html`:

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

O Latte também não escapa de todos os objetos que implementam a interface `Latte\HtmlStringable`. Assim, você pode criar sua própria classe cujo método `__toString()` retornará o código HTML que não escapará automaticamente:

```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]
O método `__toString` deve retornar o HTML correto e fornecer parâmetros que escapem, caso contrário pode ocorrer uma vulnerabilidade XSS!


Como estender o Latte com filtros, etiquetas, etc. .[#toc-how-to-extend-latte-with-filters-tags-etc]
====================================================================================================

Como adicionar um filtro personalizado, função, etiqueta, etc. ao Latte? Descubra no capítulo [que estende o Latte |extending Latte].
Se você quiser reutilizar suas alterações em diferentes projetos ou se quiser compartilhá-las com outros, você deve então [criar uma extensão |creating-extension].


Qualquer código no modelo `{php ...}` .{toc: RawPhpExtension}
=============================================================

Somente expressões PHP podem ser escritas dentro do [`{do}` |tags#do] por isso não é possível, por exemplo, inserir construções como `if ... else` ou declarações em ponto-e-vírgula.

No entanto, você pode registrar a extensão `RawPhpExtension`, que adiciona a tag `{php ...}`. Você pode usá-la para inserir qualquer código PHP. Ela não está sujeita a nenhuma regra do modo sandbox, portanto, o uso é de responsabilidade do autor do modelo.

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


Verificação do código gerado .[#toc-checking-generated-code]{data-version:3.0.7}
================================================================================

O Latte compila modelos em código PHP. Naturalmente, ele garante que o código gerado seja sintaticamente válido. Entretanto, ao usar extensões de terceiros ou RawPhpExtension, o Latte não pode garantir a exatidão do arquivo gerado.
Além disso, no PHP, você pode escrever um código sintaticamente correto, mas que é proibido (por exemplo, atribuir um valor à variável $this) e causar um erro de compilação do PHP.
Se você escrever uma operação desse tipo em um modelo, ela também será incluída no código PHP gerado. Como há mais de duzentas operações diferentes proibidas no PHP, o Latte não tem como objetivo detectá-las. O próprio PHP as sinalizará durante a renderização, o que geralmente não é um problema.

Entretanto, há situações em que você deseja saber, durante a compilação do modelo, se ele não contém erros de compilação do PHP. Especialmente quando os modelos podem ser editados pelos usuários ou quando você usa o [Sandbox |Sandbox]. Nesse caso, faça com que os modelos sejam verificados durante a compilação.
Você pode ativar essa funcionalidade usando o método Engine::enablePhpLint(). Como ele precisa chamar o binário PHP para a verificação, passe seu caminho como parâmetro:

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

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// captura erros de Latte e também erros de compilação em PHP
	echo 'Error: ' . $e->getMessage();
}
```


Modo estrito .[#toc-strict-mode]{data-version:3.0.8}
====================================================

No modo de análise rigorosa, o Latte verifica a ausência de tags HTML de fechamento e também desativa o uso da variável `$this`. Para ativá-lo:

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

Para gerar modelos com o cabeçalho `declare(strict_types=1)`, faça o seguinte:

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


Tradução em Modelos .{toc: TranslatorExtension}
===============================================

Use a extensão `TranslatorExtension` para adicionar [`{_...}` |tags#_], [`{translate}` |tags#translate] e filtro [`translate` |filters#translate] para o modelo. Eles são usados para traduzir valores ou partes do modelo para outros idiomas. O parâmetro é o método (chamado PHP) que realiza a tradução:

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

	public function translate(string $original): string
	{
		// criar $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);
```

O tradutor é chamado no momento da execução, quando o modelo é renderizado. Entretanto, o Latte pode traduzir todos os textos estáticos durante a compilação do modelo. Isto economiza desempenho porque cada string é traduzida apenas uma vez e a tradução resultante é escrita no arquivo compilado. Isto cria múltiplas versões compiladas do modelo no diretório do cache, uma para cada idioma. Para fazer isto, basta especificar o idioma como segundo parâmetro:

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

Por texto estático entendemos, por exemplo, `{_'hello'}` ou `{translate}hello{/translate}`. Textos não estáticos, como `{_$foo}`, continuarão a ser traduzidos em tempo de execução.

O modelo também pode passar parâmetros adicionais para o tradutor via `{_$original, foo: bar}` ou `{translate foo: bar}`, que recebe como a matriz `$params`:

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


Depuração e Tracy .[#toc-debugging-and-tracy]
=============================================

Latte tenta tornar o desenvolvimento tão agradável quanto possível. Para fins de depuração, existem três tags [`{dump}` |tags#dump], [`{debugbreak}` |tags#debugbreak] e [`{trace}` |tags#trace].

Você terá o maior conforto se instalar a grande [ferramenta de depuração Tracy |tracy:] e ativar o plugin Latte:

```php
// permite que Tracy
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// ativa a extensão da Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
```

Agora você verá todos os erros em uma bela tela vermelha, incluindo erros em modelos com destaque de linha e coluna ([vídeo |https://github.com/nette/tracy/releases/tag/v2.9.0]).
Ao mesmo tempo, no canto inferior direito da chamada Barra Tracy, aparece uma aba para Latte, onde você pode ver claramente todos os modelos renderizados e suas relações (incluindo a possibilidade de clicar no modelo ou código compilado), bem como as variáveis:

[* latte-debugging.webp *]

Como o Latte compila os modelos em código PHP legível, você pode passar convenientemente por eles em seu IDE.


Linter: Validação da sintaxe do gabarito .{toc: Linter}
=======================================================

A ferramenta Linter o ajudará a percorrer todos os modelos e verificar a existência de erros de sintaxe. Ela é lançada a partir do console:

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

Use o parâmetro `--strict` para ativar [o modo estrito|#strict mode].

Se você usa etiquetas personalizadas, crie também seu Linter personalizado, por exemplo, `custom-latte-lint`:

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

// digite o caminho real para o arquivo autoload.php
require __DIR__ . '/vendor/autoload.php';

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

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// adicione suas extensões individuais aqui
$latte->addExtension(/* ... */);

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

Como alternativa, você pode passar seu próprio objeto `Latte\Engine` para o Linter:

```php
$latte = new Latte\Engine;
// Aqui configuramos o objeto $latte
$linter = new Latte\Tools\Linter(engine: $latte);
```


Carregando modelos a partir de uma corda .[#toc-loading-templates-from-a-string]
================================================================================

Necessidade de carregar modelos de cordas em vez de arquivos, talvez para fins de teste? [O StringLoader |extending-latte#stringloader] o ajudará:

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

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


Manipulador de Exceções .[#toc-exception-handler]
=================================================

Você pode definir seu próprio manipulador para as exceções esperadas. Exceções levantadas no interior [`{try}` |tags#try] e na [caixa de areia |sandbox] são passados para ela.

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

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


Procura automática de layout .[#toc-automatic-layout-lookup]
============================================================

Usando a etiqueta [`{layout}` |template-inheritance#layout-inheritance] o modelo determina seu modelo pai. Também é possível ter o layout pesquisado automaticamente, o que simplificará a escrita dos modelos, uma vez que eles não precisarão incluir a tag `{layout}`.

Isto é conseguido da seguinte forma:

```php
$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// ele retorna o caminho para o arquivo de modelo pai
		return 'automatic.layout.latte';
	}
};

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

Se o modelo não deve ter um layout, ele o indicará com a tag `{layout none}`.

Práticas para os desenvolvedores

Instalação

A melhor maneira de instalar o Latte é usar um Composer:

composer require latte/latte

Versões PHP suportadas (aplica-se às últimas versões de correção Latte):

versão compatível com PHP

PHP 3.0 | Latte 3.0 | PHP 8.0 – 8.2

Como fazer um modelo

Como fazer um modelo? Basta usar este código simples:

$latte = new Latte\Engine;
// diretório de cache
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* template variables */ ];
// ou $params = novos parâmetros TemplateParameters(/* ... */);

// render à saída
$latte->render('template.latte', $params);
// ou render a variável
$output = $latte->renderToString('template.latte', $params);

Os parâmetros podem ser arrays ou até melhor objeto, o que proporcionará verificação de tipo e sugestão no editor.

Você também pode encontrar exemplos de uso no repositório de exemplos Latte.

Desempenho e Caching

Os modelos Latte são extremamente rápidos, porque o Latte os compila diretamente em código PHP e os armazena em cache em disco. Assim, eles não têm despesas extras em comparação com os modelos escritos em PHP puro.

O cache é automaticamente regenerado cada vez que você muda o arquivo fonte. Assim, você pode editar convenientemente seus modelos Latte durante o desenvolvimento e ver as mudanças imediatamente no navegador. Você pode desativar este recurso em um ambiente de produção e economizar um pouco de desempenho:

$latte->setAutoRefresh(false);

Quando implantada em um servidor de produção, a geração inicial de cache, especialmente para aplicações maiores, pode, compreensivelmente, demorar um pouco. O Latte tem prevenção embutida contra a „debandada do cache:https://en.wikipedia.org/…che_stampede“. Esta é uma situação em que o servidor recebe um grande número de solicitações simultâneas e como o cache do Latte ainda não existe, todos eles o gerariam ao mesmo tempo. O que aumenta a CPU. O Latte é inteligente, e quando há várias solicitações simultâneas, apenas o primeiro tópico gera o cache, os outros esperam e depois o utilizam.

Parâmetros como classe

Melhor do que passar variáveis para o modelo como matrizes é criar uma classe. Você recebe notação de segurança de tipo, boa sugestão no IDE e uma forma de registrar filtros e funções.

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

Desativação do Auto-Escaping da variável

Se a variável contém uma string HTML, você pode marcá-la para que Latte não fuja automaticamente (e portanto duplique) dela. Isto evita a necessidade de especificar |noescape no modelo.

A maneira mais fácil é enrolar o cordel em um objeto Latte\Runtime\Html:

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

O Latte também não escapa de todos os objetos que implementam a interface Latte\HtmlStringable. Assim, você pode criar sua própria classe cujo método __toString() retornará o código HTML que não escapará automaticamente:

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

O método __toString deve retornar o HTML correto e fornecer parâmetros que escapem, caso contrário pode ocorrer uma vulnerabilidade XSS!

Como estender o Latte com filtros, etiquetas, etc.

Como adicionar um filtro personalizado, função, etiqueta, etc. ao Latte? Descubra no capítulo que estende o Latte. Se você quiser reutilizar suas alterações em diferentes projetos ou se quiser compartilhá-las com outros, você deve então criar uma extensão.

Qualquer código no modelo {php ...}

Somente expressões PHP podem ser escritas dentro do {do} por isso não é possível, por exemplo, inserir construções como if ... else ou declarações em ponto-e-vírgula.

No entanto, você pode registrar a extensão RawPhpExtension, que adiciona a tag {php ...}. Você pode usá-la para inserir qualquer código PHP. Ela não está sujeita a nenhuma regra do modo sandbox, portanto, o uso é de responsabilidade do autor do modelo.

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

Verificação do código gerado

O Latte compila modelos em código PHP. Naturalmente, ele garante que o código gerado seja sintaticamente válido. Entretanto, ao usar extensões de terceiros ou RawPhpExtension, o Latte não pode garantir a exatidão do arquivo gerado. Além disso, no PHP, você pode escrever um código sintaticamente correto, mas que é proibido (por exemplo, atribuir um valor à variável $this) e causar um erro de compilação do PHP. Se você escrever uma operação desse tipo em um modelo, ela também será incluída no código PHP gerado. Como há mais de duzentas operações diferentes proibidas no PHP, o Latte não tem como objetivo detectá-las. O próprio PHP as sinalizará durante a renderização, o que geralmente não é um problema.

Entretanto, há situações em que você deseja saber, durante a compilação do modelo, se ele não contém erros de compilação do PHP. Especialmente quando os modelos podem ser editados pelos usuários ou quando você usa o Sandbox. Nesse caso, faça com que os modelos sejam verificados durante a compilação. Você pode ativar essa funcionalidade usando o método Engine::enablePhpLint(). Como ele precisa chamar o binário PHP para a verificação, passe seu caminho como parâmetro:

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

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// captura erros de Latte e também erros de compilação em PHP
	echo 'Error: ' . $e->getMessage();
}

Modo estrito

No modo de análise rigorosa, o Latte verifica a ausência de tags HTML de fechamento e também desativa o uso da variável $this. Para ativá-lo:

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

Para gerar modelos com o cabeçalho declare(strict_types=1), faça o seguinte:

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

Tradução em Modelos

Use a extensão TranslatorExtension para adicionar {_...}, {translate} e filtro translate para o modelo. Eles são usados para traduzir valores ou partes do modelo para outros idiomas. O parâmetro é o método (chamado PHP) que realiza a tradução:

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

	public function translate(string $original): string
	{
		// criar $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);

O tradutor é chamado no momento da execução, quando o modelo é renderizado. Entretanto, o Latte pode traduzir todos os textos estáticos durante a compilação do modelo. Isto economiza desempenho porque cada string é traduzida apenas uma vez e a tradução resultante é escrita no arquivo compilado. Isto cria múltiplas versões compiladas do modelo no diretório do cache, uma para cada idioma. Para fazer isto, basta especificar o idioma como segundo parâmetro:

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

Por texto estático entendemos, por exemplo, {_'hello'} ou {translate}hello{/translate}. Textos não estáticos, como {_$foo}, continuarão a ser traduzidos em tempo de execução.

O modelo também pode passar parâmetros adicionais para o tradutor via {_$original, foo: bar} ou {translate foo: bar}, que recebe como a matriz $params:

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

Depuração e Tracy

Latte tenta tornar o desenvolvimento tão agradável quanto possível. Para fins de depuração, existem três tags {dump}, {debugbreak} e {trace}.

Você terá o maior conforto se instalar a grande ferramenta de depuração Tracy e ativar o plugin Latte:

// permite que Tracy
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// ativa a extensão da Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

Agora você verá todos os erros em uma bela tela vermelha, incluindo erros em modelos com destaque de linha e coluna (vídeo). Ao mesmo tempo, no canto inferior direito da chamada Barra Tracy, aparece uma aba para Latte, onde você pode ver claramente todos os modelos renderizados e suas relações (incluindo a possibilidade de clicar no modelo ou código compilado), bem como as variáveis:

Como o Latte compila os modelos em código PHP legível, você pode passar convenientemente por eles em seu IDE.

Linter: Validação da sintaxe do gabarito

A ferramenta Linter o ajudará a percorrer todos os modelos e verificar a existência de erros de sintaxe. Ela é lançada a partir do console:

vendor/bin/latte-lint <path>

Use o parâmetro --strict para ativar o modo estrito.

Se você usa etiquetas personalizadas, crie também seu Linter personalizado, por exemplo, custom-latte-lint:

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

// digite o caminho real para o arquivo autoload.php
require __DIR__ . '/vendor/autoload.php';

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

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// adicione suas extensões individuais aqui
$latte->addExtension(/* ... */);

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

Como alternativa, você pode passar seu próprio objeto Latte\Engine para o Linter:

$latte = new Latte\Engine;
// Aqui configuramos o objeto $latte
$linter = new Latte\Tools\Linter(engine: $latte);

Carregando modelos a partir de uma corda

Necessidade de carregar modelos de cordas em vez de arquivos, talvez para fins de teste? O StringLoader o ajudará:

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

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

Manipulador de Exceções

Você pode definir seu próprio manipulador para as exceções esperadas. Exceções levantadas no interior {try} e na caixa de areia são passados para ela.

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

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

Procura automática de layout

Usando a etiqueta {layout} o modelo determina seu modelo pai. Também é possível ter o layout pesquisado automaticamente, o que simplificará a escrita dos modelos, uma vez que eles não precisarão incluir a tag {layout}.

Isto é conseguido da seguinte forma:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// ele retorna o caminho para o arquivo de modelo pai
		return 'automatic.layout.latte';
	}
};

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

Se o modelo não deve ter um layout, ele o indicará com a tag {layout none}.