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.

Você vai aprender
  • 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 error mais 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 ser TXT, 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 comando echo.
$ 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 <<<EOTNowdoc <<<'EOT'
Parecido comAspas duplas "…"Aspas simples '…'
Variáveis ($nome, {$a['x']})TrocadasLiterais
Escapes (\n, \t, \$)FuncionamLiterais
Aspas " e ' no textoLivresLivres
Uso típicoHTML, e-mails, mensagens com dadosExemplos 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.

Antes do PHP 7.3

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
Nunca coloque dados do usuário dentro do SQL

É 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çãoUse
Texto curto, uma linhaAspas duplas ou simples
Texto de várias linhas com variáveisHeredoc <<<TXT
Texto de várias linhas sem variáveis, ou com $ literaisNowdoc <<<'TXT'
Página HTML inteiraHTML direto com <?= ?> (veja PHP Dentro do HTML)
Números formatados no meio do textoCalcule 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.

Esqueceu o ; 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.

O fechamento tem outro nome (ou não existe)
<?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).

Uma linha do texto começa com o identificador
<?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.

Invalid body indentation level
<?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.

Invalid indentation - tabs and spaces cannot be mixed
$ 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

EscritaO 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 fechamentoNão faz parte do texto

Exercícios

1. Abrir um heredoc

Complete a abertura do heredoc chamado TXT:

echo

2. Nowdoc

Complete a abertura de um nowdoc chamado CODIGO:

echo

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, e EOT;. 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.