'conteúdo raw'] */ private array $cache = []; // ------------------------------------------------------------------------- /** * @param string $dir Diretório dos templates (padrão: 'template/') * @param string $ext Extensão dos arquivos (padrão: '.tpl') * * @throws RuntimeException se o diretório não existir */ public function __construct(string $dir = 'template', string $ext = '.tpl') { if (!is_dir($dir)) { throw new RuntimeException( "Template: diretório não encontrado — \"{$dir}\"" ); } $this->dir = rtrim($dir, '/\\'); $this->ext = '.' . ltrim($ext, '.'); } // ------------------------------------------------------------------------- /** * Renderiza um template e retorna o HTML como string * * @param string $name Nome do template (sem extensão) * @param array $data Variáveis para substituir no template * @param bool $minify Minifica o resultado * * @throws RuntimeException se o arquivo não existir * @throws InvalidArgumentException se $data não for array * * @example * $tpl = new Template(); * echo $tpl->render('home', ['titulo' => 'Olá Mundo']); */ public function render(string $name, array $data = [], bool $minify = false): string { $raw = $this->load($name); $output = $this->replace($raw, $data); if ($minify) { $output = $this->minify($output); } return $output; } /** * Renderiza e envia direto para o output com o Content-Type correto * * @param string $name * @param array $data * @param bool $minify * @param string $contentType Ex: 'text/html; charset=UTF-8' * * @example * $tpl->output('home', ['titulo' => 'Olá'], minify: true); * $tpl->output('style', [], contentType: 'text/css'); */ public function output( string $name, array $data = [], bool $minify = false, string $contentType = 'text/html; charset=UTF-8' ): void { if (headers_sent($file, $line)) { throw new RuntimeException( "Template: headers já enviados em {$file}:{$line}" ); } header('Content-Type: ' . $contentType); echo $this->render($name, $data, $minify); } // ------------------------------------------------------------------------- // CACHE // ------------------------------------------------------------------------- /** * Pré-carrega um ou mais templates na memória * * @example * $tpl->preload(['header', 'footer', 'home']); */ public function preload(array $names): void { foreach ($names as $name) { $this->load($name); } } /** * Limpa o cache em memória */ public function clearCache(?string $name = null): void { if ($name === null) { $this->cache = []; } else { unset($this->cache[$name]); } } // ------------------------------------------------------------------------- // PRIVADOS // ------------------------------------------------------------------------- /** * Carrega o template do disco ou do cache em memória * * @throws RuntimeException se o arquivo não existir */ private function load(string $name): string { if (isset($this->cache[$name])) { return $this->cache[$name]; } $path = $this->dir . DIRECTORY_SEPARATOR . $name . $this->ext; if (!file_exists($path)) { throw new RuntimeException( "Template: arquivo não encontrado — \"{$name}{$this->ext}\"" ); } $this->cache[$name] = file_get_contents($path); return $this->cache[$name]; } /** * Substitui os placeholders {chave} pelos valores de $data * Placeholders sem correspondência são mantidos no output */ private function replace(string $template, array $data): string { foreach ($data as $key => $value) { $template = str_replace('{' . $key . '}', (string) $value, $template); } return $template; } /** * Minifica HTML, CSS ou JS de forma segura * * - Remove comentários HTML * - Remove comentários CSS /* ... */ * - Colapsa espaços e quebras de linha entre tags HTML * - NÃO toca em conteúdo dentro de
,