Search by

jonatasrt / template

jonatasrt

Template engine PHP 8+ sem código PHP no HTML: variáveis, blocos, objetos e modificadores. Compatível com a sintaxe do raelgc/template.

Package info

github.com/jonatasrt/template

pkg:composer/jonatasrt/template

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v3.0.0 2026-08-25 12:42 UTC

This package is auto-updated.

Last update: 2026-08-25 13:35:24 UTC


README

Engine de template para PHP 8.0+ que mantém o HTML completamente livre de código PHP.

Esta biblioteca é uma reescrita moderna da clássica raelgc/template (criada por Rael Gugelmin Cunha), com compatibilidade total de sintaxe: as mesmas variáveis, os mesmos blocos, os mesmos modificadores, o mesmo suporte a objetos e comentários.

Você não precisa reescrever nenhum arquivo HTML. Qualquer template que funcionava na versão antiga funciona aqui, sem nenhuma alteração.

Índice

Requisitos

  • PHP >= 8.0 (testado em 8.0, 8.1, 8.2, 8.3 e 8.4)
  • Nenhuma extensão além das padrão (pcre, json)

Não há warnings, notices nem deprecations mesmo com error_reporting(E_ALL).

Instalação

Via Composer

composer require jonatasrt/template
<?php
require_once("vendor/autoload.php");
use jonatasrt\view\Template;

Via download / cópia manual

Baixe o repositório e inclua o arquivo diretamente:

<?php
require_once("lib/jonatasrt/view/Template.php");
use jonatasrt\view\Template;

Migrando do raelgc/template

O HTML não muda em nada. No PHP, só existem duas possibilidades:

Opção 1 — trocar o use (recomendado):

// antes
use raelgc\view\Template;
// depois
use jonatasrt\view\Template;

Opção 2 — nem mexer no use: crie um alias logo depois de carregar a biblioteca e todo o código antigo continua funcionando sem nenhuma alteração:

require_once("vendor/autoload.php");
class_alias('jonatasrt\view\Template', 'raelgc\view\Template');

A partir daí, use raelgc\view\Template; e new raelgc\view\Template(...) seguem válidos.

Se por algum motivo a versão antiga ainda estiver carregada no mesmo processo, não há conflito: a função global replace() é declarada dentro de um function_exists().

Olá Mundo

hello.html:

<html>
  <body>
    Olá {FULANO}!
  </body>
</html>

hello.php:

<?php

require_once("vendor/autoload.php");
use jonatasrt\view\Template;

$tpl = new Template("hello.html");
$tpl->FULANO = "Mundo";
$tpl->show();

Resultado:

<html>
  <body>
    Olá Mundo!
  </body>
</html>

Variáveis

Toda variável de template é escrita entre chaves: {NOME_DA_VARIAVEL}.

  • São aceitos apenas caracteres alfanuméricos e o underscore (_).
  • São case sensitive: {FULANO} é diferente de {fulano}.
  • São identificadas automaticamente na leitura do arquivo — não é preciso declarar nada.
  • Variáveis não preenchidas são removidas do resultado final (não sobra {VARIAVEL} na tela).
$tpl->NOME  = "Maria";
$tpl->IDADE = 32;          // int, float e bool são convertidos automaticamente
$tpl->NADA  = null;        // vira string vazia
$tpl->TAGS  = ['a','b'];   // arrays viram "a, b"

Atribuir uma variável que não existe no HTML lança uma exceção:

$tpl->INEXISTENTE = "x";   // RuntimeException: var INEXISTENTE does not exist

Esse comportamento é intencional: ele avisa cedo sobre erros de digitação.

Checando se variáveis existem

if ($tpl->exists("FULANO")) {
    $tpl->FULANO = "Maria";
}

// isset() também funciona
if (isset($tpl->FULANO)) {
    $tpl->FULANO = "Maria";
}

Variáveis dinâmicas

Quando o nome da variável só é conhecido em tempo de execução, use chaves:

$varname = "fulano";
$tpl->{"NOME_" . strtoupper($varname)} = "Maria";   // preenche {NOME_FULANO}

Variáveis com modificadores

É possível aplicar funções PHP direto no HTML, usando |:

{TEXTO|nl2br}
{NOME|strtoupper}
{VALOR|number_format:2:',':'.'}
{TEXTO|trim|strtoupper}

Regras:

  • O valor da variável é sempre passado como primeiro argumento da função.
  • Parâmetros extras vêm depois, separados por :.
  • Modificadores podem ser encadeados (aplicados da esquerda para a direita).
  • Qualquer função callable do PHP (ou sua própria função) pode ser usada.

Como str_replace() recebe o texto no terceiro parâmetro, a biblioteca já registra a função global replace(), com o texto em primeiro lugar:

{TEXTO|replace:Bar:Baz}

Um modificador inexistente lança BadFunctionCallException.

Blocos

Blocos são trechos do HTML que podem ser repetidos, ou simplesmente omitidos. São delimitados por comentários HTML:

<!-- BEGIN NOME_DO_BLOCO -->
   ... conteúdo ...
<!-- END NOME_DO_BLOCO -->
<html>
  <body>
    <table>
      <!-- BEGIN BLOCK_DADOS -->
      <tr>
        <td>{NOME}</td>
        <td>{QUANTIDADE}</td>
      </tr>
      <!-- END BLOCK_DADOS -->
    </table>
  </body>
</html>
$produtos = [
    ["nome" => "Sabão em Pó",     "quantidade" => 15],
    ["nome" => "Escova de Dente", "quantidade" => 53],
    ["nome" => "Creme Dental",    "quantidade" => 37],
];

foreach ($produtos as $p) {
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");     // "imprime" uma linha
}

$tpl->show();

Pontos importantes:

  • Um bloco só aparece se block() for chamado. Se nunca for chamado, ele simplesmente não existe no HTML final — é assim que se faz conteúdo condicional.
  • Nomes de blocos são identificados automaticamente e devem ser únicos no template (blocos duplicados lançam UnexpectedValueException).
  • Chamar block() para um bloco inexistente lança InvalidArgumentException.
  • Um bloco mal formado (sem END, por exemplo) lança UnexpectedValueException.

Blocos aninhados

Blocos podem conter outros blocos, sem limite de profundidade:

<!-- BEGIN BLOCK_PRODUTOS -->
<table>
  <!-- BEGIN BLOCK_DADOS -->
  <tr><td>{NOME}</td><td>{QUANTIDADE}</td></tr>
  <!-- END BLOCK_DADOS -->
</table>
<!-- END BLOCK_PRODUTOS -->

Sempre que o bloco pai é exibido, os blocos filhos acumulados são "descarregados" e limpos, prontos para a próxima iteração.

Blocos automáticos por padrão

Se um bloco aninhado é exibido, todos os blocos pais são exibidos automaticamente. Ou seja, no exemplo acima basta chamar block("BLOCK_DADOS"): o BLOCK_PRODUTOS aparece sozinho.

foreach ($produtos as $p) {
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");
}
// BLOCK_PRODUTOS não precisa ser chamado
$tpl->show();

Blocos FINALLY

Um bloco FINALLY é exibido apenas quando o bloco correspondente nunca foi exibido. É o "senão" do template — perfeito para mensagens de lista vazia:

<!-- BEGIN BLOCK_DADOS -->
<tr><td>{NOME}</td><td>{QUANTIDADE}</td></tr>
<!-- END BLOCK_DADOS -->
<tr><td colspan="2">Nenhum produto cadastrado.</td></tr>
<!-- FINALLY BLOCK_DADOS -->
foreach ($produtos as $p) {           // se $produtos estiver vazio...
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");
}
$tpl->show();                          // ...a mensagem do FINALLY aparece sozinha

Sem if, sem count(), sem código extra.

Blocos com HTML select

O caso clássico de "qual opção está selecionada":

<select name="estado">
  <!-- BEGIN BLOCK_ESTADO -->
  <option value="{SIGLA}" {SELECTED}>{NOME}</option>
  <!-- END BLOCK_ESTADO -->
</select>
$estados = ["RS" => "Rio Grande do Sul", "SC" => "Santa Catarina", "PR" => "Paraná"];
$atual = "SC";

foreach ($estados as $sigla => $nome) {
    $tpl->SIGLA = $sigla;
    $tpl->NOME  = $nome;
    if ($sigla == $atual) $tpl->SELECTED = "selected";
    else                  $tpl->clear("SELECTED");    // limpa para a próxima volta
    $tpl->block("BLOCK_ESTADO");
}

O método clear() é essencial aqui: sem ele, o valor da iteração anterior continuaria valendo.

Usando vários arquivos HTML

Ideal para separar cabeçalho, rodapé e miolo:

base.html:

<html>
  <body>
    <h1>Meu site</h1>
    {CONTEUDO}
  </body>
</html>

miolo.html:

<p>Olá {FULANO}!</p>
$tpl = new Template("base.html");
$tpl->addFile("CONTEUDO", "miolo.html");
$tpl->FULANO = "Maria";
$tpl->show();

As variáveis e blocos do arquivo incluído passam a fazer parte do mesmo template — você continua usando $tpl->VARIAVEL e $tpl->block(...) normalmente.

addFile() também aceita arquivos .php (.php5, .php7, .php8, .phtml, .cgi): nesse caso o arquivo é executado e sua saída vira o valor da variável.

Guardando o conteúdo do template

$conteudo = $tpl->parse();               // retorna a string final
file_put_contents("arquivo.html", $conteudo);

// echo $tpl; também funciona (equivale a $tpl->show())

Usando objetos

Objetos podem ser atribuídos direto às variáveis de template — muito útil com ORMs (Doctrine, Eloquent, etc.):

<p>{USUARIO->nome}, {USUARIO->email}</p>
<p>Cidade: {USUARIO->endereco->cidade}</p>
$tpl->USUARIO = $usuario;

A ordem de resolução de cada propriedade é:

  1. getter (getNome()) — aceita camelCase e snake_case: {OBJ->minha_prop} chama getMinhaProp()
  2. método mágico __get()
  3. propriedade pública com o nome normalizado
  4. propriedade pública com o nome exato
  5. getter booleano (isAtivo())
  6. ArrayAccess ($obj['chave'])

Se nenhum acessor for encontrado, é lançada BadMethodCallException com a mensagem exata do que falta — em vez do erro fatal de acesso a propriedade privada.

Se o objeto no meio da cadeia for null, o resultado é string vazia (não quebra).

Detalhes:

  • Objeto com __toString(): o valor é usado direto em {VARIAVEL} (sem ->).
  • Objeto sem __toString() usado como {VARIAVEL}: vira Object: {"json":"do objeto"}, o que ajuda muito na hora de depurar.
  • Modificadores funcionam em propriedades: {USUARIO->nome|strtoupper}.

Comentários

Comentários de template usam três traços e nunca chegam ao HTML final:

<!--- Este comentário não aparece no resultado --->

Comentários HTML normais (<!-- ... -->) são preservados.

Escapando variáveis

Para manter uma variável literal no HTML final (útil quando você gera templates a partir de templates), insira {_} logo depois da chave de abertura:

{{_}CONTEUDO}

O resultado final conterá {CONTEUDO}.

Criando XML, CSV e outros formatos

A classe não é específica de HTML — ela trabalha com texto. Você pode usar arquivos .xml, .csv, .txt, .svg, .json ou qualquer outro formato, com a mesma sintaxe de variáveis e blocos (os delimitadores de bloco usam sintaxe de comentário HTML/XML).

$tpl = new Template("relatorio.csv");
header("Content-Type: text/csv");
$tpl->show();

Mensagens de erro

Exceção Quando acontece
InvalidArgumentException arquivo não existe / está vazio; block() de bloco inexistente; addFile() em variável inexistente
RuntimeException atribuir ou ler variável que não existe no template
UnexpectedValueException bloco duplicado; bloco mal formado (BEGIN sem END)
BadMethodCallException não existe acessor no objeto para a propriedade usada
BadFunctionCallException modificador que não é uma função callable

Como todas herdam de Exception, dá para tratar tudo junto:

try {
    $tpl = new Template("base.html");
    $tpl->FULANO = "Maria";
    $tpl->show();
} catch (Exception $e) {
    error_log($e->getMessage());
}

Precisão e desempenho

A biblioteca evita expressões regulares no caminho quente — o trabalho pesado é feito com str_replace(), o que a torna bem mais rápida que engines antigas do estilo PHPLib. Não há cache em disco, e nem é necessário: em praticamente todo sistema real o gargalo é o banco de dados, não o template.

Um efeito colateral disso é que tabulações no início dos blocos são mantidas no HTML final (elas não afetam a renderização). Se você precisa de uma reprodução fiel — por exemplo, dentro de <pre> ou <code> — use o segundo parâmetro do construtor:

$tpl = new Template("base.html", true);   // modo "accurate" (mais lento, saída exata)

Referência da API

Método Descrição
__construct(string $filename, bool $accurate = false) Cria o template a partir do arquivo principal
$tpl->VARIAVEL = $valor Define o valor de uma variável (aceita string, int, float, bool, null, array e objeto)
$tpl->VARIAVEL Lê o valor atual de uma variável
exists(string $varname): bool Informa se a variável existe no template
clear(string $varname): void Limpa o valor de uma variável
addFile(string $varname, string $filename): void Carrega outro arquivo dentro de uma variável
block(string $block, bool $append = true): void Exibe (acumula) um bloco
setParent(string $parent, string $block): void Associa manualmente um bloco filho a um pai
parse(): string Retorna o conteúdo final
show(): void Imprime o conteúdo final

Licença

LGPL-2.1-or-later — a mesma da biblioteca original. Você pode usá-la como biblioteca inclusive em projetos comerciais.

Créditos ao autor original: Rael Gugelmin Cunha (raelgc/template).