Práticas do desenvolvedor

Instalação

A melhor maneira de instalar o Latte é usando o Composer:

composer require latte/latte

Versões do PHP suportadas (aplica-se às últimas versões de patch do Latte):

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

Como renderizar um template

Como renderizar um template? Basta este código simples:

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

$params = [ /* variáveis do template */ ];
// ou $params = new TemplateParameters(/* ... */);

// renderiza na saída
$latte->render('template.latte', $params);
// renderiza para uma variável
$output = $latte->renderToString('template.latte', $params);

Os parâmetros podem ser um array ou, melhor ainda, um objeto, que garante a verificação de tipo e sugestões em editores.

Exemplos de uso também podem ser encontrados no repositório Latte examples.

Desempenho e cache

Os templates em Latte são extremamente rápidos, pois o Latte os compila diretamente em código PHP e os armazena em cache no disco. Portanto, eles não têm nenhuma sobrecarga adicional em comparação com templates escritos em PHP puro.

O cache é regenerado automaticamente sempre que você altera o arquivo de origem. Durante o desenvolvimento, você pode editar confortavelmente os templates em Latte e ver as alterações imediatamente no navegador. Você pode desativar esta função no ambiente de produção para economizar um pouco de desempenho:

$latte->setAutoRefresh(false);

Ao implantar em um servidor de produção, a geração inicial do cache, especialmente para aplicações maiores, pode, compreensivelmente, levar um momento. Latte tem prevenção integrada contra “cache stampede”. Esta é uma situação em que um grande número de requisições simultâneas chega, que iniciam o Latte, e como o cache ainda não existe, todas começariam a gerá-lo simultaneamente. O que sobrecarregaria indevidamente o servidor. Latte é inteligente e, com múltiplas requisições simultâneas, apenas a primeira thread gera o cache, as outras esperam e depois o utilizam.

Parâmetros como classe

Melhor do que passar variáveis para o template como um array é criar uma classe. Você obterá assim uma escrita type-safe, sugestões úteis no IDE e um caminho para registro de 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,
));

Desativando o auto-escaping de variável

Se uma variável contiver uma string em HTML, você pode marcá-la para que o Latte não a escape automaticamente (e, portanto, duplamente). Você evitará assim a necessidade de especificar |noescape no template.

A maneira mais simples é envolver a string em um objeto Latte\Runtime\Html:

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

Latte também não escapa todos os objetos que implementam a interface Latte\HtmlStringable. Você pode, assim, criar sua própria classe, cujo método __toString() retornará código HTML que não será escapado 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 HTML correto e garantir o escape de parâmetros, caso contrário, pode ocorrer uma vulnerabilidade XSS!

Como estender o Latte com filtros, tags, etc.

Como adicionar seu próprio filtro, função, tag, etc. ao Latte? Isso é abordado no capítulo estendendo o Latte. Se você deseja reutilizar suas modificações em diferentes projetos ou compartilhá-las com outros, você deve criar uma extensão.

Código arbitrário no template {php ...}

Dentro da tag {do}, apenas expressões PHP podem ser escritas, então você não pode, por exemplo, inserir construções como if ... else ou declarações terminadas com ponto e vírgula.

No entanto, você pode registrar a extensão Latte\Essential\RawPhpExtension, que adiciona a tag {php ...}. Com ela, você pode inserir qualquer código PHP. Nenhuma regra do modo sandbox se aplica a ela, portanto, o uso é de responsabilidade do autor do template.

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

Verificação do código gerado

Latte compila templates em código PHP. Obviamente, ele garante que o código gerado seja sintaticamente válido. No entanto, ao usar extensões de terceiros ou RawPhpExtension, Latte não pode garantir a correção do arquivo gerado. Também é possível escrever código em PHP que, embora sintaticamente correto, é proibido (por exemplo, atribuir um valor à variável $this) e causa um Erro de Compilação do PHP. Se você escrever tal operação em um template, ela também chegará ao código PHP gerado. Como existem cerca de duzentas operações proibidas diferentes em PHP, Latte não tem a ambição de detectá-las. O próprio PHP as sinalizará apenas durante a renderização, o que geralmente não é um problema.

No entanto, existem situações em que você deseja saber já no momento da compilação do template que ele não contém nenhum Erro de Compilação do PHP. Especialmente quando os templates podem ser editados por usuários, ou você usa Sandbox. Nesse caso, faça com que os templates sejam verificados já no momento da compilação. Esta funcionalidade é ativada pelo método Engine::enablePhpLint(). Como ele precisa chamar o binário PHP para a verificação, passe o caminho para ele como parâmetro:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/usr/bin/php'); // Exemplo de caminho

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// captura erros no Latte e também Compile Error no PHP
	echo 'Erro: ' . $e->getMessage();
}

Localidade

Latte permite definir a localidade, que afeta a formatação de números, datas e ordenação. É definida usando o método setLocale(). O identificador de localidade segue o padrão IETF language tag, que usa a extensão PHP intl. Consiste no código do idioma e, opcionalmente, no código do país, por exemplo, en_US para inglês nos Estados Unidos, de_DE para alemão na Alemanha, pt_BR para português no Brasil, etc.

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

A configuração da localidade afeta os filtros localDate, sort, number e bytes.

Requer a extensão PHP intl. A configuração no Latte não afeta a configuração global de localidade no PHP.

Modo estrito

No modo de análise estrito, o Latte verifica se as tags HTML de fechamento não estão faltando e também proíbe o uso da variável $this. Você o ativa assim:

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

A geração de templates com o cabeçalho declare(strict_types=1) é ativada assim:

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

Tradução em templates

Usando a extensão Latte\Essential\TranslatorExtension, você adiciona as tags {_...}, {translate} e o filtro translate ao template. Eles servem para traduzir valores ou partes do template para outros idiomas. Como parâmetro, especificamos o método (PHP callable) que realiza a tradução:

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

	public function translate(string $original): string
	{
		// de $original criamos $translated de acordo com $this->lang
		return $translated;
	}
}

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

O tradutor é chamado em tempo de execução ao renderizar o template. No entanto, o Latte pode traduzir todos os textos estáticos já durante a compilação do template. Isso economiza desempenho, pois cada string é traduzida apenas uma vez e a tradução resultante é escrita na forma compilada. No diretório de cache, são criadas várias versões compiladas do template, uma para cada idioma. Para isso, basta especificar o idioma como segundo parâmetro:

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

Texto estático significa, por exemplo, {_'hello'} ou {translate}hello{/translate}. Textos não estáticos, como {_$foo}, continuarão sendo traduzidos em tempo de execução.

Também é possível passar parâmetros adicionais do template para o tradutor usando {_$original, foo: bar} ou {translate foo: bar}, que ele recebe como um array $params:

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

Depuração e Tracy

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

Você obterá o maior conforto se instalar também a excelente ferramenta de depuração Tracy e ativar o complemento para Latte:

// ativa o Tracy
Tracy\Debugger::enable();

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

Agora, todos os erros serão exibidos em uma tela vermelha clara, incluindo erros nos templates com destaque de linha e coluna (vídeo). Ao mesmo tempo, no canto inferior direito, na chamada Tracy Bar, aparecerá uma aba para Latte, onde todos os templates renderizados e suas relações mútuas são claramente visíveis (incluindo a possibilidade de clicar para ir ao template ou ao código compilado) e também as variáveis:

Como o Latte compila templates em código PHP claro, você pode depurá-los confortavelmente em seu IDE.

Linter: validação da sintaxe dos templates

Para percorrer todos os templates e verificar se eles não contêm erros de sintaxe, a ferramenta Linter o ajudará. Ela é executada a partir do console:

vendor/bin/latte-lint <caminho>

O parâmetro --strict ativa o modo estrito.

Se você usa tags personalizadas, crie também sua própria versão do Linter, por exemplo, custom-latte-lint:

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

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

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

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

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

Alternativamente, 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 templates de string

Precisa carregar templates de strings em vez de arquivos, talvez para fins de teste? 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ção

Você pode definir seu próprio manipulador para exceções esperadas. As exceções que ocorrem dentro de {try} e no sandbox serão passadas para ele.

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

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

Busca automática de layout

Usando a tag {layout}, o template especifica seu template pai. Também é possível fazer com que o layout seja buscado automaticamente, o que simplificará a escrita dos templates, pois não será necessário especificar a tag {layout} neles.

Isso é alcançado da seguinte maneira:

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

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

Se o template não deve ter um layout, ele o indica com a tag {layout none}.

versão: 3.0