PHP Heredoc e Nowdoc
Quando o texto tem várias linhas (um trecho de HTML, uma consulta SQL, um e-mail), escrever tudo com aspas e \n vira uma bagunça. O PHP tem uma forma própria para textos longos: o heredoc, que funciona como aspas duplas de várias linhas, e o nowdoc, que funciona como aspas simples.
- A anatomia do heredoc:
<<<, o identificador e a linha de fechamento. - A diferença entre heredoc (
<<<EOT, com variáveis) e nowdoc (<<<'EOT', literal). - Como a indentação do fechamento é removida do texto (PHP 7.3+).
- Usos práticos: HTML, SQL e e-mails.
- Os
Parse errormais comuns e como ler cada um.
O problema: texto longo com aspas
A Ana quer mandar um recado de várias linhas para o aluno Bruno. Com aspas duplas, fica assim:
<?php $aluno = "Bruno"; echo "Olá, $aluno!\n\nSua nota foi 8.5.\nO \"recado\" da professora: continue assim!\n";
$ php recado-aspas.php Olá, Bruno! Sua nota foi 8.5. O "recado" da professora: continue assim!
Funciona, mas é difícil de ler: onde termina uma linha? E cada aspa dupla dentro do texto precisa de \". O heredoc deixa você escrever o texto do jeito que ele vai aparecer.
Heredoc: aspas duplas de várias linhas
O nome vem do inglês here document, "documento aqui": o texto está ali, no meio do código. A mesma ideia existe no Bash e em outras linguagens.
<?php $aluno = "Bruno"; $nota = 8.5; echo <<<EOT Olá, $aluno! Sua nota foi $nota. O "recado" da professora: continue assim! EOT;
- <<<
- Três sinais de "menor que": "começa um heredoc".
- EOT
- O identificador: uma palavra que você escolhe para marcar o fim.
EOT(end of text, "fim do texto") é tradição, mas pode serTXT,HTML,SQL,FIM… Mesmas regras de um nome de variável: letras, números e_, sem começar com número. Por costume, em maiúsculas. Nada pode vir depois dele na linha. - As linhas do meio
- O texto, exatamente como vai sair. Variáveis são trocadas (como nas aspas duplas) e as aspas, simples ou duplas, são só texto: nada de
\". - EOT;
- A linha de fechamento: o mesmo identificador, sozinho no começo da linha (ou só com espaços antes), seguido do
;que termina o comandoecho.
$ php recado.php Olá, Bruno! Sua nota foi 8.5. O "recado" da professora: continue assim!
Repare num detalhe: a última linha do texto não termina com quebra de linha. A quebra que vem antes de EOT; não faz parte do texto. Se você quiser uma quebra no fim, deixe uma linha em branco antes do fechamento ou faça echo <<<EOT … EOT, "\n";.
Tudo o que funciona nas aspas duplas funciona no heredoc
Variáveis, chaves, arrays, objetos e sequências de escape, as mesmas regras da aula de Interpolação:
<?php
$nome = "Ana";
$aluno = ["nome" => "Bruno", "nota" => 8.5];
echo <<<EOT
Olá, $nome!
Aluno: {$aluno['nome']}, nota {$aluno['nota']}.
Preço: R\$ 10\tfim
Aspas "duplas" e 'simples' sem escape.
EOT;
echo "\n---\n";
echo <<<'EOT'
Olá, $nome!
Preço: R\$ 10\tfim
EOT;
echo "\n";
$ php heredoc.php Olá, Ana! Aluno: Bruno, nota 8.5. Preço: R$ 10 fim « \$ virou $ e \t virou um tab Aspas "duplas" e 'simples' sem escape. --- Olá, $nome! « nowdoc: nada é trocado Preço: R\$ 10\tfim « nem os escapes
A segunda parte já é o nowdoc, a próxima seção.
Missão: reescreva o recado usando um heredoc, com as variáveis $aluno e $nota, para a saída ser:Olá, Bruno!Sua nota foi 8.5.
<?php $aluno = "Bruno"; $nota = 8.5; echo "Olá, aluno!\nSua nota foi ...";
<?php $aluno = "Bruno"; $nota = 8.5; echo <<<EOT Olá, $aluno! Sua nota foi $nota. EOT;
Nowdoc: aspas simples de várias linhas
O nowdoc é escrito igual, mas com o identificador entre aspas simples: <<<'EOT'. Dentro dele, nada é trocado: nem variáveis, nem \n, nem \$. O texto sai exatamente como está, como nas aspas simples.
- <<<'EOT'
- As aspas simples em volta do identificador transformam o heredoc em nowdoc. Só na abertura: o fechamento continua
EOT;, sem aspas.
O nome é um trocadilho: here doc / now doc. Use quando o texto tem muitos $ que devem aparecer literalmente, como um trecho de código PHP ou de JavaScript que você quer mostrar numa página de tutorial:
<?php
echo <<<'CODIGO'
Para mostrar o nome, escreva:
echo "Olá, $nome!";
CODIGO;
echo "\n";
$ php exemplo-codigo.php
Para mostrar o nome, escreva:
echo "Olá, $nome!";
Com heredoc, o PHP tentaria trocar $nome (e avisaria Undefined variable $nome). Também existe <<<"EOT", com aspas duplas: é exatamente igual ao heredoc sem aspas, só deixa mais claro que é um heredoc.
Heredoc <<<EOT | Nowdoc <<<'EOT' | |
|---|---|---|
| Parecido com | Aspas duplas "…" | Aspas simples '…' |
Variáveis ($nome, {$a['x']}) | Trocadas | Literais |
Escapes (\n, \t, \$) | Funcionam | Literais |
Aspas " e ' no texto | Livres | Livres |
| Uso típico | HTML, e-mails, mensagens com dados | Exemplos de código, textos com muitos $ |
Indentação do fechamento PHP 7.3+
Dentro de uma função ou de um if, o código fica recuado (indentado). Desde o PHP 7.3, o fechamento também pode ser recuado, e o PHP remove esse recuo de todas as linhas do texto:
<?php
function recado($nome) {
$texto = <<<TXT
Olá, $nome!
Recuo extra.
Até mais.
TXT;
return $texto;
}
echo recado("Ana"), "|\n";
$ php recuo.php Olá, Ana! Recuo extra. « os 2 espaços A MAIS que o fechamento ficaram Até mais.| « o | mostra onde o texto termina
O TXT; está recuado 8 espaços. O PHP tirou 8 espaços do começo de cada linha. A linha "Recuo extra." tinha 10, então ficaram 2. A regra: a posição do fechamento define a margem esquerda do texto.
Funções (function, return) são o assunto de Funções; aqui elas só servem para mostrar o código recuado.
O fechamento precisava estar na coluna 1, sem nenhum espaço antes, e ser a única coisa na linha (além do ;). Em códigos antigos você vai ver heredocs "colados na margem", quebrando a indentação do resto. No PHP 8, recue à vontade.
O fechamento no meio da expressão
Também desde o 7.3, o fechamento pode ser seguido de mais código na mesma linha, como uma vírgula ou um parêntese. Assim dá para passar um heredoc direto para uma função:
<?php
$frutas = ["maçã", "uva"];
$lista = implode(", ", $frutas);
echo strtoupper(<<<TXT
compras: $lista
TXT), "\n";
$total = 3 * 4.5;
$t = <<<"TXT"
Total: $total
TXT;
echo $t, "\n";
$ php argumento.php COMPRAS: MAçã, UVA « o heredoc foi o argumento do strtoupper (sem mb_: veja a aula de mbstring) Total: 13.5
Como na interpolação, contas não são feitas dentro do texto: a Ana calculou $total antes. E o strtoupper deixou o "çã" minúsculo, o problema dos acentos da aula Acentos e mbstring (com mb_strtoupper sairia MAÇÃ).
Usos práticos
Gerar HTML
É o uso mais comum. O HTML fica legível, com as variáveis no lugar:
<?php
$produto = htmlspecialchars("Maçã");
$preco = "4,50";
echo <<<HTML
<div class="cartao">
<h2>$produto</h2>
<p>R$ $preco o quilo</p>
</div>
HTML;
$ php cartao.php <div class="cartao"> <h2>Maçã</h2> <p>R$ 4,50 o quilo</p> </div>
No terminal você vê as tags; no navegador, o cartão desenhado. Com htmlspecialchars antes, um nome de produto com < ou & não quebra a página (veja Escapar a Saída). Para páginas inteiras, a forma mais comum em PHP é outra: sair do PHP com ?> e escrever o HTML direto (veja PHP Dentro do HTML). O heredoc brilha em pedaços de HTML montados dentro de uma função.
Experimente: rode e veja o cartão na aba Resultado. Troque o produto e o preço, ou acrescente uma linha <p>Estoque: $estoque kg</p> com uma variável nova.
<?php
$produto = htmlspecialchars("Maçã");
$preco = "4,50";
echo <<<HTML
<div style="border:1px solid #ccc; padding:8px; width:200px">
<h2>$produto</h2>
<p>R$ $preco o quilo</p>
</div>
HTML;
Consultas SQL
Consultas a banco de dados (o assunto do Nível 2) costumam ter várias linhas. O heredoc com recuo deixa tudo alinhado no código:
<?php
$sql = <<<SQL
SELECT nome, nota
FROM alunos
WHERE nota >= 7
ORDER BY nome
SQL;
echo $sql, "\n";
$ php consulta.php SELECT nome, nota FROM alunos WHERE nota >= 7 ORDER BY nome
É tentador escrever WHERE nome = '$nome' num heredoc. Se $nome vier de um formulário, isso abre a porta para o ataque mais famoso da web, a SQL injection. Use o heredoc só para o texto fixo da consulta e passe os valores com prepared statements (veja Prepared Statements e SQL Injection).
E-mails e mensagens
<?php
$aluno = "Bruno";
$nota = 8.5;
$email = <<<EMAIL
Olá, $aluno!
Sua nota final foi $nota.
Parabéns pela aprovação!
Professora Ana
EMAIL;
echo $email, "\n";
$ php email.php Olá, Bruno! Sua nota final foi 8.5. Parabéns pela aprovação! Professora Ana
As linhas em branco do meio fazem parte do texto. Repare que o identificador pode ser qualquer palavra que descreva o conteúdo: HTML, SQL, EMAIL. Alguns editores até colorem o texto conforme o nome (HTML como HTML, SQL como SQL).
Qual usar?
| Situação | Use |
|---|---|
| Texto curto, uma linha | Aspas duplas ou simples |
| Texto de várias linhas com variáveis | Heredoc <<<TXT |
Texto de várias linhas sem variáveis, ou com $ literais | Nowdoc <<<'TXT' |
| Página HTML inteira | HTML direto com <?= ?> (veja PHP Dentro do HTML) |
| Números formatados no meio do texto | Calcule antes com number_format ou sprintf e coloque a variável no heredoc |
Quando dá errado
Quase todo erro com heredoc é um Parse error: o PHP não consegue nem ler o arquivo, e nada roda. A linha indicada costuma ser depois do problema, porque o PHP só percebe que algo está errado mais adiante.
; depois do fechamento
<?php $t = <<<TXT Olá TXT echo $t;
$ php sem-ponto.php Parse error: syntax error, unexpected token "echo" in /home/ana/php/sem-ponto.php on line 5
O que significa: "token echo inesperado na linha 5". Sem o ;, o PHP achou que o comando da linha 2 continuava, e tropeçou no echo.
Como consertar: TXT;. O erro aponta a linha 5, mas o problema está na 4.
<?php $t = <<<TXT Olá FIM; echo $t;
$ php sem-fim.php Parse error: syntax error, unexpected end of file in /home/ana/php/sem-fim.php on line 6
O que significa: "fim de arquivo inesperado". O heredoc foi aberto com TXT, e o PHP procurou uma linha TXT até o fim do arquivo. FIM; e o echo viraram parte do texto. O arquivo tem 5 linhas; a "linha 6" é o fim dele.
Como consertar: use o mesmo identificador na abertura e no fechamento (maiúsculas e minúsculas contam).
<?php $t = <<<FIM Começo FIM do texto, só que não FIM; echo $t;
$ php fim-cedo.php Parse error: syntax error, unexpected token "do" in /home/ana/php/fim-cedo.php on line 4
O que significa: desde o PHP 7.3, o fechamento pode ser seguido de outras coisas na mesma linha. Então a linha FIM do texto… fechou o heredoc, e o PHP tentou ler do texto… como código (do é até uma palavra do PHP, do laço do-while).
Como consertar: escolha um identificador que nunca aparece no começo de uma linha do texto: TXT, EOT, HTML.
<?php
$t = <<<TXT
linha 1
linha 2
TXT;
echo $t;
$ php recuo-errado.php Parse error: Invalid body indentation level (expecting an indentation level of at least 4) in /home/ana/php/recuo-errado.php on line 4
O que significa: "nível de recuo do corpo inválido (esperava pelo menos 4)". O fechamento está recuado 4 espaços, então o PHP precisa tirar 4 espaços de cada linha. A linha 4 só tem 2.
Como consertar: nenhuma linha do texto pode estar mais à esquerda que o fechamento. Recue a linha ou puxe o fechamento para a esquerda.
$ php recuo-misto.php Parse error: Invalid indentation - tabs and spaces cannot be mixed in /home/ana/php/recuo-misto.php on line 3
O que significa: "recuo inválido: tabs e espaços não podem ser misturados". Uma linha estava recuada com a tecla Tab e outra com espaços. Na tela parecem iguais, mas para o PHP não são.
Como consertar: configure o editor para usar só espaços (o padrão recomendado em PHP é 4 espaços; veja Editores e IDEs) e refaça o recuo.
Referência
| Escrita | O que é |
|---|---|
<<<EOT … EOT; | Heredoc: várias linhas, com variáveis e escapes |
<<<"EOT" … EOT; | Heredoc (forma com aspas duplas, igual à anterior) |
<<<'EOT' … EOT; | Nowdoc: várias linhas, tudo literal |
| Fechamento recuado 7.3+ | O recuo do fechamento é removido de todas as linhas |
EOT), EOT, 7.3+ | O fechamento pode ser seguido de mais código |
| A quebra antes do fechamento | Não faz parte do texto |
Exercícios
1. Abrir um heredoc
Complete a abertura do heredoc chamado TXT:
2. Nowdoc
Complete a abertura de um nowdoc chamado CODIGO:
3. Com $nome = "Ana";, o que um nowdoc com a linha Olá, $nome mostra?
4. O fechamento TXT; está recuado 4 espaços e uma linha do texto tem 6 espaços no começo. Quantos espaços sobram nessa linha na saída?
5. O PHP mostrou Parse error: syntax error, unexpected end of file num arquivo com heredoc. Qual a causa mais provável?
Missão final: o cartão abaixo mostra as palavras "produto" e "preco" em vez dos valores. Troque a fruta para "Uva", o preço para "7,90" e faça o heredoc usar as variáveis, para a página mostrar Uva e R$ 7,90 o quilo.
<?php
$produto = htmlspecialchars("Maçã");
$preco = "4,50";
echo <<<HTML
<div class="cartao">
<h2>produto</h2>
<p>R$ preco o quilo</p>
</div>
HTML;
<?php
$produto = htmlspecialchars("Uva");
$preco = "7,90";
echo <<<HTML
<div class="cartao">
<h2>$produto</h2>
<p>R$ $preco o quilo</p>
</div>
HTML;
Resumo
- Heredoc:
<<<EOT, o texto em várias linhas, eEOT;. Funciona como aspas duplas: variáveis e escapes são trocados, aspas são texto comum. - Nowdoc:
<<<'EOT'. Funciona como aspas simples: tudo literal. - O identificador é qualquer nome (
TXT,HTML,SQL); o fechamento tem que ser igual. - Desde o PHP 7.3, o fechamento pode ser recuado (o recuo sai de todas as linhas) e pode ser seguido de mais código (
TXT)). - A quebra de linha antes do fechamento não entra no texto.
- Ótimo para HTML, SQL (sem dados do usuário dentro!) e e-mails.