Menu

Formulários em PHP: processar, validar e reexibir

Um formulário PHP envia para si mesmo com <form method="post">, e o script lê os campos de $_POST['name'] quando $_SERVER['REQUEST_METHOD'] é 'POST'. Veja validação, mensagens de erro ao lado dos campos, manter os valores digitados, checkboxes e selects, e como escapar a saída.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um formulário PHP envia de volta para o script que o imprimiu: escreva <form method="post"> com inputs que têm nome, e quando $_SERVER['REQUEST_METHOD'] for 'POST', leia cada campo como $_POST['name']. Escape tudo o que você imprimir de volta com htmlspecialchars().

Rode este bloco, digite um nome no formulário e clique em Send: o mesmo script roda de novo, desta vez com $_POST preenchido.

Três coisas acontecem aqui que todo processador de formulário precisa:

  • A verificação do método. Na primeira visita a requisição é um GET e não há nada a processar. O ?? 'GET' só importa quando o arquivo roda pela linha de comando, onde REQUEST_METHOD não está definido; num servidor web ele sempre está.
  • ?? '' em cada campo. Um campo pode faltar (um input renomeado, uma requisição enviada por um script), e ler uma chave ausente imprime um aviso.
  • Escapar na saída. O nome volta para a página duas vezes, e o htmlspecialchars() garante que um valor como <b>Ada</b> apareça como texto em vez de virar marcação. Os detalhes estão na página sobre htmlspecialchars.

Validar um formulário e mostrar erros ao lado dos campos

Reúna os erros num array com o nome do campo como chave. Se o array estiver vazio, o formulário é válido; senão, imprima o formulário de novo com cada mensagem embaixo do seu campo e a entrada do visitante ainda no lugar. Envie vazio, depois com um e-mail inválido, depois com valores válidos.

O novalidate desliga as verificações do próprio navegador para você ver as do PHP funcionando. Num formulário real, mantenha as duas: a validação do navegador é uma conveniência para o visitante, a validação do PHP é a que vale, porque qualquer pessoa pode enviar uma requisição sem o seu formulário. O filter_var() faz o e-mail e o intervalo inteiro com uma chamada cada; a página sobre filter_var mostra os outros filtros dele.

Checkboxes, botões de opção e select

Um campo de texto sempre envia um valor, mesmo vazio. Os outros controles não:

  • Um checkbox desmarcado não envia nada, então a chave dele não aparece em $_POST.
  • Checkboxes com o nome topics[] chegam como array: $_POST['topics'] é ['php', 'sql'].
  • Um grupo de botões de opção ou um <select> envia o value da opção escolhida.

Para manter a escolha depois do envio, imprima checked ou selected na opção que bate. Marque algumas caixas, escolha um nível e clique em Save.

As duas verificações depois de ler a entrada importam mais do que parece. O array_intersect() descarta qualquer tópico que não estava na sua lista, e a verificação com is_string() mais array_key_exists() volta o nível para um padrão se alguém enviar um valor que você nunca ofereceu (ou um array, que faria $levels[$level] lançar um erro). Um select limita o que um navegador pode enviar, não o que uma requisição pode conter.

Formulários em várias etapas com campos ocultos

Um input oculto leva um valor de uma etapa para a próxima sem mostrá-lo. Aqui a etapa 1 pede um nome, a etapa 2 pede uma linguagem e envia o nome junto num campo oculto, e a etapa 3 mostra os dois. Passe pelas três etapas.

Campos ocultos aparecem no código-fonte da página e podem ser editados como qualquer outro campo, então servem para levar as próprias respostas do visitante entre as etapas, mas nunca um preço, um id de usuário ou um papel. Mantenha esses no servidor, numa sessão; veja sessões e cookies.

Post/Redirect/Get: evitar o reenvio ao atualizar

Depois de um POST bem-sucedido, se o script imprimir uma página de "obrigado" diretamente, apertar atualizar faz o navegador enviar o POST de novo, e o pedido ou comentário é salvo duas vezes. A correção é salvar, redirecionar e mostrar o resultado num GET:

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $errors = validate($_POST);
    if (!$errors) {
        save_comment($_POST);
        $_SESSION['flash'] = 'Comment posted.';
        header('Location: /comments.php', true, 303);
        exit;
    }
    // On errors, fall through and print the form with the messages.
}

$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);

303 See Other diz ao navegador para seguir o redirecionamento com um GET. O exit depois do header() importa: sem ele o resto do script continua rodando. O redirecionamento também precisa acontecer antes de qualquer saída, senão o PHP se recusa com "headers already sent".

Formulários reais também adicionam um token CSRF: um valor aleatório guardado na sessão e num campo oculto, comparado com hash_equals() no envio, para que outro site não consiga enviar o seu formulário em nome de um visitante logado.

Testar a validação sem navegador

Coloque as regras numa função que recebe um array e retorna os erros. A página a chama com $_POST, e você pode chamá-la com o array que quiser, o que facilita testar os casos extremos. Adicione os seus próprios casos à lista.

O quarto caso é o que as pessoas esquecem. Uma requisição pode enviar name[]=x, o que torna $_POST['name'] um array, e passar um array para o trim() lança um TypeError e para o script. Verificar is_string() antes transforma uma entrada hostil num erro de validação comum. O cast (string) dos blocos anteriores também evita o TypeError, mas transforma o array na palavra Array e imprime um aviso, então is_string() é a verificação mais limpa.

Erro comum: ler $_POST antes de o formulário ser enviado

O primeiro script de formulário mais comum lê os campos no topo do arquivo:

<?php
$name = $_POST['name'];
echo "Hello, $name";

Na primeira visita nada foi enviado, então o PHP imprime:

Warning: Undefined array key "name" in /var/www/form.php on line 2

A correção é o padrão do primeiro bloco: processe o formulário só quando a requisição for um POST, e leia cada campo com um padrão, $_POST['name'] ?? ''. Se o aviso aparecer depois do envio, compare o atributo name no HTML com a chave no PHP: name="Name" e $_POST['name'] são chaves diferentes.

Perguntas frequentes

Como envio um formulário PHP para a mesma página?

Escreva <form method="post"> sem action (ou com action=""). O navegador envia de volta para a URL atual, então o mesmo script mostra o formulário e o processa: verifique $_SERVER['REQUEST_METHOD'] === 'POST' e leia os campos de $_POST.

Como mantenho os valores do formulário depois do envio no PHP?

Imprima o valor enviado de volta no campo, escapado: <input name="email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">. Para um checkbox imprima checked, e para uma opção de select imprima selected, quando o valor enviado bater.

Como pego os valores de vários checkboxes no PHP?

Dê aos checkboxes o mesmo nome terminando em colchetes, name="topics[]". O PHP então coloca os valores marcados num array, $_POST['topics']. Caixas desmarcadas não enviam nada, então use $_POST['topics'] ?? [] e compare cada valor com a lista que você ofereceu.

Por que atualizar a página reenvia meu formulário PHP?

A última requisição foi um POST, então o navegador a repete ao atualizar. Depois de um envio bem-sucedido, redirecione com header('Location: /thanks.php'); exit; (o padrão Post/Redirect/Get). A atualização então repete um GET inofensivo.

É seguro usar $_POST no PHP?

Não, todo valor em $_POST vem do visitante e pode ser qualquer coisa, incluindo HTML, SQL ou um array onde você esperava uma string. Valide na entrada (filter_var, verificações de tamanho e de lista permitida) e escape na saída (htmlspecialchars para HTML, prepared statements para SQL).

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR