Pular para o conteúdo
murad.guide

PHP

Capítulo 11

Funções pequenas e texto seguro no HTML

6 min de leitura 1.042 palavras 1 gravura

Neste capítulo você vai reutilizar tarefas e mostrar texto recebido como texto.

Seu ponto de partida

Estude depois do capítulo 10. O exemplo deste capítulo é 11-funcoes-saida, no pacote que acompanha o volume. Copie a pasta inicio para um diretório de prática e preserve o original. A raiz do exemplo é a referência concluída desta etapa. Sua cópia permite experimentar sem perder a referência.

Execute o PHP na raiz da sua cópia com php -S 127.0.0.1:8000 -t public. Abra http://127.0.0.1:8000/. Não abra PHP por duplo clique nem use o servidor de Python. Encerre com Ctrl+C antes de trocar de etapa.

Como praticar

Leia a explicação e digite ou confira o código indicado. Mude uma coisa. Antes de conferir, anote sua previsão. Salve, recarregue e compare. Se não acontecer o esperado, investigue o erro antes de avançar. Cada experiência parte dos valores da referência. Depois de conferir, desfaça somente a mudança desse experimento antes de começar a próxima lição. Você pode parar depois de uma lição e retomar mais tarde.

Reconheça uma função com entrada e resultado

Uma função agrupa instruções com um nome. Ela pode receber um valor, o parâmetro, e devolver um resultado com return. Isso evita copiar as mesmas instruções em cada saída da página.

Na referência, apoio.php guarda tarefas pequenas. Index.php carrega esse arquivo com require. O caminho parte de DIR, a pasta do arquivo em execução, e sobe um nível para o apoio fora de public.

String indica um valor de texto. A palavra depois dos dois-pontos indica que a função devolve texto. As funções de apoio estão completas nos arquivos fornecidos; você vai reconhecer suas tarefas e alterar um uso por vez.

Faça no seu exemplo

Abra apoio.php na raiz da cópia e confira a função escapar.

function escapar(string $texto): string
{
    return htmlspecialchars($texto, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8");
}

Mude uma coisa e preveja

No início de public/index.php, troque o texto fictício da mensagem para Texto com marcação.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

Recarregue. Os sinais e a palavra strong devem aparecer como texto; a mensagem não deve virar negrito.

Se não acontecer

Se a marcação for aplicada, confira se a saída usa escapar. Não retire os sinais da mensagem para fingir que o problema foi resolvido.

Prepare a saída no contexto certo

Htmlspecialchars transforma caracteres que têm função no HTML em representações de texto. ENT_QUOTES inclui aspas simples e duplas; ENT_SUBSTITUTE trata sequências inválidas na saída. UTF-8 identifica a codificação usada pela página.

Escapar na saída conserva o que foi escrito sem deixar esse texto virar uma nova tag. Isso é diferente de apagar caracteres durante a leitura. O mesmo preparo serve para texto normal do HTML e para o valor entre aspas de um atributo, como value.

Ele não é um preparo universal para endereços, JavaScript ou consultas de banco. Neste volume, o texto entra somente nos contextos HTML mostrados. Uma URL recebida não será colocada livremente em href.

Faça no seu exemplo

No article demonstrativo, confira a chamada antes da saída.

<p><?= escapar($mensagem) ?></p>

Mude uma coisa e preveja

Troque a mensagem por Lia & Bia conferiram as “aspas”.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

Recarregue. Você deve ler o texto completo com ampersand e aspas. <?= é uma forma curta de echo, usada para mostrar o resultado da função.

Se não acontecer

Se aparecerem códigos de entidade escritos por extenso, pode haver escape duplicado. Guarde texto normal na variável e prepare uma vez na saída.

Leia um campo sem aceitar qualquer tipo

Campo é a função de apoio que lê um valor por chave. Ela aceita somente texto, confere UTF-8 e retira espaços nas pontas com trim. Quando o valor não existe ou tem outro tipo, devolve texto vazio.

O PHP pode receber um array quando alguém usa nome[]=Lia numa consulta ou num envio construído manualmente. Converter esse array diretamente em texto provoca avisos e não cria um nome válido.

Devolver vazio aqui evita o aviso. A próxima etapa de validação vai recusar o campo vazio antes de gravar. Ler um valor com cuidado não significa que já está autorizado para qualquer tarefa.

Faça no seu exemplo

No início de public/index.php, a consulta nome usa a função campo. Abra o arquivo de apoio para ver a implementação completa.

$nome = campo($_GET, "nome");
$nome = $nome === "" ? "Lia" : $nome;

Mude uma coisa e preveja

Abra index.php?nome=%3CLia%3E e depois index.php?nome[]=Lia.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

No primeiro caso, o nome deve mostrar como texto. No segundo, a alternativa Lia deve aparecer sem aviso de conversão de array.

Se não acontecer

Se houver aviso Array to string conversion, restaure a leitura por campo. Não use uma conversão forçada para aceitar qualquer tipo.

Trecho do resultado de referência do capítulo 11

A captura mostra um trecho da referência concluída. Abra e percorra o exemplo para conferir o restante. Experimentos temporários podem produzir outra aparência; restaure os valores indicados antes de comparar.

As funções de apoio completas

<?php
declare(strict_types=1);

function escapar(string $texto): string
{
    return htmlspecialchars($texto, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8");
}

function campo(array $dados, string $chave): string
{
    $valor = $dados[$chave] ?? "";
    if (!is_string($valor) || preg_match('//u', $valor) !== 1) {
        return "";
    }
    return trim($valor);
}

function tamanho(string $texto): int
{
    if (function_exists("mb_strlen")) {
        return mb_strlen($texto, "UTF-8");
    }
    return preg_match_all('/./us', $texto);
}

Atividades para fazer antes das respostas

Use uma nova cópia do exemplo. Tente aplicar o que aprendeu antes de consultar os comentários.

  1. Mostre uma frase com sinais de menor e maior.

  2. Explique a tarefa de return.

  3. Abra a consulta sem nome e confira a alternativa.

Respostas comentadas

Compare sua explicação e o que realmente aconteceu. Outra solução pode funcionar se cumprir o objetivo e preservar o acesso ao conteúdo.

  1. Use texto normal na variável e escapar na saída. Os sinais devem ser visíveis e não iniciar elementos.

  2. Return entrega o resultado da função à expressão que a chamou. Ele não imprime sozinho.

  3. Campo devolve vazio e o index usa Lia como alternativa fictícia.

Quando seguir

Você entende por que ler, validar e preparar a saída são tarefas distintas. Se ainda precisa de ajuda, repita a experiência pequena antes de avançar.

Consulta opcional

Não é preciso ler os links externos para executar os arquivos fornecidos. Eles servem para consultar detalhes quando você quiser.

O arquivo completo

Referência do exemplo 11-funcoes-saida, reproduzida aqui para que você possa conferir sem sair da página.

apoio.php

<?php
declare(strict_types=1);

function escapar(string $texto): string
{
    return htmlspecialchars($texto, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8");
}

function campo(array $dados, string $chave): string
{
    $valor = $dados[$chave] ?? "";
    if (!is_string($valor) || preg_match('//u', $valor) !== 1) {
        return "";
    }
    return trim($valor);
}

function tamanho(string $texto): int
{
    if (function_exists("mb_strlen")) {
        return mb_strlen($texto, "UTF-8");
    }
    return preg_match_all('/./us', $texto);
}

public/estilos.css

* { box-sizing: border-box; }
body { margin: 0; background: #f4f7fb; color: #203246;
  font: 1.05rem/1.65 Arial, sans-serif; }
header, main, footer { max-width: 48rem; margin-inline: auto; padding: 1.5rem; }
header { padding-top: 2.5rem; }
h1 { font-size: clamp(1.8rem, 5vw, 2.7rem); line-height: 1.2; }
main { background: #fff; border-radius: .8rem; }
article { border-top: 1px solid #cad4df; padding-block: 1rem; }
a { color: #124e77; text-underline-offset: .2em; }
nav { display: flex; flex-wrap: wrap; gap: 1rem; }
label { display: block; font-weight: bold; }
input:not([type=hidden]), textarea { width: 100%; max-width: 34rem;
  border: 2px solid #718095; border-radius: .35rem; padding: .7rem;
  font: inherit; color: inherit; background: #fff; }
button { font: inherit; background: #124e77; color: white;
  border: 0; border-radius: .35rem; padding: .75rem 1.2rem; cursor: pointer; }
button:hover { background: #103b59; }
.mensagem { white-space: pre-wrap; overflow-wrap: anywhere; }
.erros { color: #8b142c; }
.ok { color: #175c30; font-weight: bold; }
:focus-visible { outline: 3px solid #b04d00; outline-offset: 3px; }
.pular { position: absolute; left: -10000px; }
.pular:focus { position: static; display: block; padding: 1rem; background: white; }
@media (max-width: 35rem) { header,main,footer { padding: 1rem; } }