PHP Validar Dados
Um formulário é uma porta aberta: qualquer pessoa, ou qualquer programa, pode mandar qualquer coisa para o seu PHP. Nome em branco, e-mail sem arroba, idade "vinte", idade 150, um array onde você esperava texto. Validar é conferir cada dado antes de usar, e responder com educação quando algo está errado. Nesta aula você monta um formulário de cadastro completo: confere os campos, mostra a mensagem de erro ao lado de cada um e não obriga a pessoa a digitar tudo de novo.
- Por que a conferência do navegador (
required,type="email") não basta. - Como tratar campos obrigatórios com
trim(tirar os espaços das pontas). - Como usar
filter_varpara e-mails (FILTER_VALIDATE_EMAIL) e números inteiros (FILTER_VALIDATE_INT), inclusive com faixa mínima e máxima. - Por que comparar com
=== false(a armadilha do zero). - Como guardar os erros num array e mostrar cada mensagem ao lado do seu campo.
- Como manter os valores que a pessoa digitou quando há erro.
- A diferença entre validar no cliente (navegador) e no servidor (PHP).
A ideia: o porteiro e a recepcionista
Pense num prédio com duas pessoas na entrada. A recepcionista fica na calçada e avisa, gentilmente: "o senhor esqueceu de preencher o nome". Ela ajuda quem está de boa-fé, mas qualquer um pode passar direto por ela. O porteiro fica na porta, confere o documento de todo mundo, e sem ele ninguém entra.
- A recepcionista é a validação no navegador (atributos
required,type="email",min,max, JavaScript): rápida e simpática, mas fácil de pular. - O porteiro é a validação no PHP: a única que vale de verdade.
Prova: pulando a recepcionista
A Ana fez um formulário com tudo o que o HTML oferece:
<form method="post" action="cadastro.php"> <input name="nome" required maxlength="60"> <input type="email" name="email" required> <input type="number" name="idade" min="1" max="120" required> <button>Cadastrar</button> </form>
No navegador, ninguém consegue enviar o nome vazio nem a idade 150. Mas o curl não é um navegador: ele manda o pedido direto para o cadastro.php, sem passar pelo formulário. E o PHP recebe exatamente o que foi mandado (aqui, com um var_dump($_POST)):
$ curl -s localhost:8000/dump.php -d "nome=&email=isso+nao+e+email&idade=150"
array(3) {
["nome"]=>
string(0) ""
["email"]=>
string(16) "isso nao e email"
["idade"]=>
string(3) "150"
}
Do mesmo jeito, qualquer pessoa pode abrir as ferramentas do navegador (F12), apagar o required e enviar. Por isso a regra:
$_GET, $_POST, $_COOKIE e $_FILES como suspeito, até o PHP conferir. O navegador ajuda o visitante; o servidor protege o sistema.Passo 1: limpar com trim
Um campo "preenchido" só com espaços parece cheio para o computador (" " não é vazio), mas não tem nada. E é comum a pessoa digitar um espaço sem querer no começo ou no fim do e-mail. A função trim ("aparar") tira espaços, tabulações e quebras de linha das pontas do texto (o meio fica intacto):
<?php
var_dump(trim(" Ana Souza "));
var_dump(trim(" "));
var_dump(trim("\tAna\n"));
$ php trim.php string(9) "Ana Souza" « o espaço do meio ficou string(0) "" « só espaços → vazio string(3) "Ana" « tab e quebra de linha também saem
Então, para um campo obrigatório, a receita é: ler com ?? '' (para não ter aviso se o campo nem veio), aparar com trim e comparar com o texto vazio:
$nome = trim($_POST['nome'] ?? '');
if ($nome === '') {
$erros['nome'] = 'Informe o seu nome.';
}
Para limitar o tamanho, use mb_strlen (que conta letras acentuadas certo; veja Acentos e mbstring): mb_strlen($nome) > 60.
Passo 2: filter_var, o conferente pronto
Conferir se um texto "tem cara de e-mail" ou "é um número inteiro" à mão é difícil e cheio de casos estranhos. O PHP já traz uma função para isso: filter_var ("filtrar variável"). Você passa o valor e o filtro, e ela devolve:
- o valor aprovado (já convertido para o tipo certo, no caso de números), ou
false, se o valor não passou.
E-mail: FILTER_VALIDATE_EMAIL
<?php
$testes = ['ana@exemplo.com', 'ana.souza@padaria.com.br', 'ana@', 'ana exemplo.com', 'ana@exemplo', '@exemplo.com', ' ana@exemplo.com'];
foreach ($testes as $t) {
echo str_pad("'$t'", 30), " → ";
var_dump(filter_var($t, FILTER_VALIDATE_EMAIL));
}
$ php email.php 'ana@exemplo.com' → string(15) "ana@exemplo.com" 'ana.souza@padaria.com.br' → string(24) "ana.souza@padaria.com.br" 'ana@' → bool(false) 'ana exemplo.com' → bool(false) 'ana@exemplo' → bool(false) '@exemplo.com' → bool(false) ' ana@exemplo.com' → bool(false) « o espaço na frente reprova: use trim antes
- filter_var(
- A função conferente.
- $t,
- O valor a conferir (aqui, cada texto da lista).
- FILTER_VALIDATE_EMAIL)
- Uma constante do PHP que escolhe o filtro: "valide como e-mail". Ela confere o formato (algo@domínio.algo), não se o e-mail existe. Para saber se existe, só mandando uma mensagem.
Inteiro: FILTER_VALIDATE_INT
<?php
$testes = ['29', '0', '-3', '29.5', 'vinte', '29 anos', ' 29 ', '', '150', '007'];
foreach ($testes as $t) {
echo str_pad("'$t'", 10), " → ";
var_dump(filter_var($t, FILTER_VALIDATE_INT));
}
$ php inteiro.php '29' → int(29) « aprovado E convertido: agora é int, não string '0' → int(0) '-3' → int(-3) '29.5' → bool(false) « tem vírgula decimal: não é inteiro 'vinte' → bool(false) '29 anos' → bool(false) « diferente do (int), que aceitaria o "29" do começo ' 29 ' → int(29) « espaços nas pontas são tolerados neste filtro '' → bool(false) '150' → int(150) '007' → bool(false) « zero à esquerda não é aceito
Repare na vantagem sobre (int): (int) "29 anos" daria 29 sem reclamar, e (int) "vinte" daria 0. O filter_var diz claramente "isso não é um inteiro".
Com faixa: min_range e max_range
O terceiro argumento do filter_var recebe opções. Para o inteiro, as opções min_range e max_range definem o menor e o maior valor aceitos:
<?php
$opcoes = ['options' => ['min_range' => 1, 'max_range' => 120]];
foreach (['29', '0', '120', '150', 'vinte'] as $t) {
echo str_pad("'$t'", 8), " → ";
var_dump(filter_var($t, FILTER_VALIDATE_INT, $opcoes));
}
$ php faixa.php '29' → int(29) '0' → bool(false) « abaixo do mínimo '120' → int(120) « os limites valem (1 e 120 são aceitos) '150' → bool(false) « acima do máximo 'vinte' → bool(false)
- ['options' => [ … ]]
- Um array com a chave
'options', e dentro dela outro array. Parece burocrático, mas é o formato que a função exige: escreva igualzinho. - 'min_range' => 1
- O menor valor aceito ("faixa mínima").
- 'max_range' => 120
- O maior valor aceito.
A armadilha do zero: compare com === false
O filter_var devolve false para reprovar, mas também pode devolver 0 para aprovar o número zero. Num if, os dois "parecem falso" (veja Booleanos):
<?php
$qtd = filter_var('0', FILTER_VALIDATE_INT);
if (!$qtd) {
echo "Errado: 0 foi tratado como inválido\n";
}
if ($qtd === false) {
echo "Inválido\n";
} else {
echo "Válido: $qtd\n";
}
$ php zero.php Errado: 0 foi tratado como inválido Válido: 0
Por isso: sempre === false para saber se o filter_var reprovou.
| Filtro | Aprova | Devolve |
|---|---|---|
FILTER_VALIDATE_EMAIL | Formato de e-mail | o texto, ou false |
FILTER_VALIDATE_INT | Inteiro (com min_range/max_range opcionais) | int, ou false |
FILTER_VALIDATE_FLOAT | Número com ponto decimal ("9.90") | float, ou false |
FILTER_VALIDATE_URL | Endereço com esquema (https://…) | o texto, ou false |
FILTER_VALIDATE_BOOL | "1", "true", "on", "yes" → true; "0", "false", "off", "no", "" e qualquer outro texto → false | bool |
Passo 3: o formulário completo
Agora juntamos tudo num cadastro com nome, e-mail e idade. A estrutura é sempre a mesma, em três partes: ler e limpar, conferir (guardando cada problema num array $erros), decidir (sem erros, segue em frente; com erros, mostra o formulário de novo).
<?php
$dados = ['nome' => '', 'email' => '', 'idade' => ''];
$erros = [];
$sucesso = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 1. Ler e limpar
$dados['nome'] = trim($_POST['nome'] ?? '');
$dados['email'] = trim($_POST['email'] ?? '');
$dados['idade'] = trim($_POST['idade'] ?? '');
// 2. Conferir cada campo
if ($dados['nome'] === '') {
$erros['nome'] = 'Informe o seu nome.';
} elseif (mb_strlen($dados['nome']) > 60) {
$erros['nome'] = 'O nome pode ter no máximo 60 letras.';
}
if ($dados['email'] === '') {
$erros['email'] = 'Informe o seu e-mail.';
} elseif (filter_var($dados['email'], FILTER_VALIDATE_EMAIL) === false) {
$erros['email'] = 'Este e-mail não parece válido.';
}
$idade = filter_var($dados['idade'], FILTER_VALIDATE_INT,
['options' => ['min_range' => 1, 'max_range' => 120]]);
if ($idade === false) {
$erros['idade'] = 'A idade deve ser um número inteiro de 1 a 120.';
}
// 3. Decidir
if (count($erros) === 0) {
$sucesso = true; // aqui você salvaria no banco, enviaria o e-mail…
}
}
?>
<!doctype html>
<html lang="pt-BR">
<head><meta charset="utf-8"><title>Cadastro</title>
<style>.erro { color: #c00; } .ok { color: #070; }</style></head>
<body>
<?php if ($sucesso): ?>
<p class="ok">Cadastro recebido, <?= htmlspecialchars($dados['nome']) ?>!</p>
<?php endif; ?>
<form method="post" novalidate>
<p><label>Nome: <input name="nome" value="<?= htmlspecialchars($dados['nome']) ?>"></label>
<?php if (isset($erros['nome'])): ?><span class="erro"><?= $erros['nome'] ?></span><?php endif; ?></p>
<p><label>E-mail: <input type="email" name="email" value="<?= htmlspecialchars($dados['email']) ?>"></label>
<?php if (isset($erros['email'])): ?><span class="erro"><?= $erros['email'] ?></span><?php endif; ?></p>
<p><label>Idade: <input type="number" name="idade" value="<?= htmlspecialchars($dados['idade']) ?>"></label>
<?php if (isset($erros['idade'])): ?><span class="erro"><?= $erros['idade'] ?></span><?php endif; ?></p>
<button>Cadastrar</button>
</form>
</body>
</html>
| Trecho | Por quê |
|---|---|
$dados = ['nome' => '', …] | Começa tudo vazio. Na primeira visita (GET), o formulário usa esses valores e aparece em branco, sem avisos de chave inexistente. |
$erros = [] | Um array associativo: a chave é o nome do campo, o valor é a mensagem. Assim cada mensagem sabe ao lado de qual campo aparecer. |
if (… === 'POST') | Só confere quando houve envio (de $_GET e $_POST). Na primeira visita, nenhuma mensagem de erro. |
if … elseif … | Para cada campo, uma mensagem só: se está vazio, nem adianta conferir o formato. |
count($erros) === 0 | Nenhum problema: pode seguir (salvar, enviar). Num sistema de verdade, aqui viria um redirecionamento (header e Redirecionamento). |
value="<?= htmlspecialchars($dados['nome']) ?>" | Mantém o que a pessoa digitou. Se o e-mail estava errado, ela corrige só o e-mail, sem redigitar o resto. O htmlspecialchars é obrigatório aqui (veja Escapar a Saída). |
isset($erros['nome']) | Mostra o <span class="erro"> só se aquele campo tem erro. |
novalidate | Desliga a conferência do navegador neste formulário, para você ver a do PHP funcionando. Num site de verdade, pode deixar as duas. |
As três situações
Primeira visita (GET): formulário vazio, nenhuma mensagem.
$ curl -s localhost:8000/cadastro.php
…
<form method="post" novalidate>
<p><label>Nome: <input name="nome" value=""></label>
</p>
<p><label>E-mail: <input type="email" name="email" value=""></label>
</p>
<p><label>Idade: <input type="number" name="idade" value=""></label>
</p>
<button>Cadastrar</button>
</form>
…
Envio com problemas: nome só com espaços, e-mail sem o ".com", idade 150.
$ curl -s localhost:8000/cadastro.php -d "nome=+++&email=ana%40exemplo&idade=150"
…
<form method="post" novalidate>
<p><label>Nome: <input name="nome" value=""></label>
<span class="erro">Informe o seu nome.</span></p>
<p><label>E-mail: <input type="email" name="email" value="ana@exemplo"></label>
<span class="erro">Este e-mail não parece válido.</span></p>
<p><label>Idade: <input type="number" name="idade" value="150"></label>
<span class="erro">A idade deve ser um número inteiro de 1 a 120.</span></p>
<button>Cadastrar</button>
</form>
…
Cada mensagem está ao lado do seu campo, e o e-mail e a idade voltaram preenchidos (value="ana@exemplo", value="150"). O nome voltou vazio porque o trim transformou os três espaços em nada.
Envio correto (com espaços sobrando no nome, que o trim resolve):
$ curl -s localhost:8000/cadastro.php -d "nome=++Ana+Souza+&email=ana%40exemplo.com&idade=29" … <body> <p class="ok">Cadastro recebido, Ana Souza!</p> <form method="post" novalidate> …
O cadastro completo, aberto com um envio cheio de problemas. Veja as mensagens na aba Resultado, corrija um campo de cada vez e envie de novo: as mensagens somem uma a uma, e o que você digitou continua lá. Experimente idade 0, 29.5, vinte.
<?php
$dados = ['nome' => '', 'email' => '', 'idade' => ''];
$erros = [];
$sucesso = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$dados['nome'] = trim($_POST['nome'] ?? '');
$dados['email'] = trim($_POST['email'] ?? '');
$dados['idade'] = trim($_POST['idade'] ?? '');
if ($dados['nome'] === '') {
$erros['nome'] = 'Informe o seu nome.';
}
if (filter_var($dados['email'], FILTER_VALIDATE_EMAIL) === false) {
$erros['email'] = 'Este e-mail não parece válido.';
}
$idade = filter_var($dados['idade'], FILTER_VALIDATE_INT,
['options' => ['min_range' => 1, 'max_range' => 120]]);
if ($idade === false) {
$erros['idade'] = 'A idade deve ser um número inteiro de 1 a 120.';
}
$sucesso = count($erros) === 0;
}
?>
<style>.erro { color: #c00; } .ok { color: #070; }</style>
<?php if ($sucesso): ?>
<p class="ok">Cadastro recebido, <?= htmlspecialchars($dados['nome']) ?>!</p>
<?php endif; ?>
<form method="post" novalidate>
<p>Nome: <input name="nome" value="<?= htmlspecialchars($dados['nome']) ?>">
<span class="erro"><?= $erros['nome'] ?? '' ?></span></p>
<p>E-mail: <input name="email" value="<?= htmlspecialchars($dados['email']) ?>">
<span class="erro"><?= $erros['email'] ?? '' ?></span></p>
<p>Idade: <input name="idade" value="<?= htmlspecialchars($dados['idade']) ?>">
<span class="erro"><?= $erros['idade'] ?? '' ?></span></p>
<button>Cadastrar</button>
</form>
Repare num atalho que a caixa acima usa: <?= $erros['nome'] ?? '' ?> escreve a mensagem se existir, ou nada. É uma alternativa mais curta ao if (isset(…)); o <span> vazio não aparece na tela.
Formas diferentes de organizar
Uma função para ler campos com segurança
Quando o formulário tem muitos campos, repetir trim($_POST['x'] ?? '') cansa. E tem um furo: se alguém mandar um array no lugar do texto (idade[]=29), o trim para o programa (veja o cenário em Quando dá errado). Uma função pequena resolve as duas coisas (funções em Funções):
function campo(string $nome): string
{
$valor = $_POST[$nome] ?? '';
return is_string($valor) ? trim($valor) : '';
}
$dados['nome'] = campo('nome');
$dados['email'] = campo('email');
Lista de opções permitidas
Para campos de escolha (<select>, radio), confira se o valor está na lista do que você ofereceu. Alguém pode mandar sabor=pizza mesmo que o <select> só tenha bolos:
$sabores = ['cenoura', 'chocolate', 'limao'];
$sabor = campo('sabor');
if (!in_array($sabor, $sabores, true)) {
$erros['sabor'] = 'Escolha um sabor da lista.';
}
O true no fim do in_array faz a comparação ser estrita (===), como você viu em Funções de Array.
Erros todos no topo, ou ao lado de cada campo?
| Forma | Como | Quando usar |
|---|---|---|
| Ao lado do campo | $erros['email'] junto do <input name="email"> | O melhor para o visitante: ele vê exatamente o que corrigir. Use sempre que puder. |
| Lista no topo | foreach ($erros as $msg) { echo "<li>$msg</li>"; } | Formulários curtos, ou em conjunto com o anterior (um resumo no topo e o detalhe ao lado). |
Validar no cliente × no servidor
| No cliente (navegador) | No servidor (PHP) | |
|---|---|---|
| Como | required, type="email", min/max, maxlength, pattern, JavaScript | trim, filter_var, in_array, mb_strlen, if |
| Quando acontece | Antes de enviar; resposta instantânea | Depois do envio; precisa ir e voltar do servidor |
| Pode ser pulada? | Sim: curl, F12, JavaScript desligado, outro programa | Não: todo pedido passa pelo PHP |
| Serve para | Conforto: avisar cedo, sem recarregar a página | Segurança e dados corretos no sistema |
| Obrigatória? | Não (mas é gentil) | Sim, sempre |
O ideal é ter as duas: o navegador avisa rápido quem errou sem querer, e o PHP barra o resto. Se for escolher uma só, é a do servidor.
Quando dá errado
trim(): Argument #1 ($string) must be of type string, array given
$ curl -s localhost:8000/cadastro.php -d "nome=Ana&email=ana%40exemplo.com&idade%5B%5D=29"
<br />
<b>Fatal error</b>: Uncaught TypeError: trim(): Argument #1 ($string) must be of type string, array given in /home/ana/site/cadastro.php:10
Stack trace:
#0 /home/ana/site/cadastro.php(10): trim(Array)
#1 {main}
thrown in <b>/home/ana/site/cadastro.php</b> on line <b>10</b><br />
O que aconteceu: alguém mandou idade[]=29 (com colchetes), e o PHP transformou a idade em array (como em name="frutas[]"). O trim só aceita texto e o programa parou com um erro fatal (Uncaught TypeError = erro de tipo não tratado). O seu formulário nunca mandaria isso; um atacante ou um robô, sim.
Como resolver: confira o tipo antes, com a função campo() mostrada acima (is_string($valor) ? trim($valor) : '').
0 foi recusada (ou aceita) sem querer
$ php zero.php Errado: 0 foi tratado como inválido Válido: 0
O que aconteceu: if (!filter_var(…)) trata o 0 aprovado como se fosse false. O mesmo acontece com empty($_POST['qtd']), que acha "0" vazio.
Como resolver: if ($qtd === false). Se o zero não for permitido, diga isso com min_range, não por acidente.
O que aconteceu: quase sempre, espaço nas pontas (' ana@exemplo.com' → bool(false), como na tabela acima). Celulares adoram colocar um espaço depois do e-mail.
Como resolver: trim antes do filter_var. E guarde o valor já aparado.
O que aconteceu: os <input> não têm value, então o navegador mostra o formulário vazio. A pessoa precisa digitar tudo de novo, e muitas desistem.
Como resolver: value="<?= htmlspecialchars($dados['campo']) ?>" em cada campo. Para <textarea>, o valor vai entre as tags; para <select>, coloque selected na opção que bate (<?= $dados['sabor'] === 'limao' ? 'selected' : '' ?>); para checkbox e radio, checked. Nunca devolva o campo de senha preenchido.
O que aconteceu: o valor foi escrito no value="…" sem htmlspecialchars. Um nome como Ana "Aninha" Souza fecha as aspas do atributo no meio, e o resto some (ou vira HTML). Esse é o começo de um ataque XSS.
Como resolver: sempre htmlspecialchars ao devolver dados para a página. A aula seguinte, Escapar a Saída (XSS), é inteira sobre isso.
Referência
| Código | O que faz |
|---|---|
trim($s) | Tira espaços, tabs e quebras de linha das pontas |
$s === '' | Campo obrigatório vazio (depois do trim) |
mb_strlen($s) > 60 | Texto longo demais (contando acentos certo) |
filter_var($s, FILTER_VALIDATE_EMAIL) | O e-mail, ou false |
filter_var($s, FILTER_VALIDATE_INT) | O int, ou false |
filter_var($s, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10]]) | Inteiro de 1 a 10, ou false |
… === false | A forma certa de saber se o filtro reprovou |
in_array($v, $permitidos, true) | O valor está na lista de opções? |
is_string($v) | Chegou texto (e não um array)? |
$erros['campo'] = 'mensagem' | Guarda o erro de um campo |
value="<?= htmlspecialchars($v) ?>" | Devolve ao campo o que foi digitado |
Exercícios
1. Obrigatório de verdade
Complete para que um nome feito só de espaços seja considerado vazio:
2. O filtro de e-mail
Complete com a constante do filtro:
3. Reprovou?
Complete a comparação que não confunde o número zero com reprovação:
4. O formulário tem <input type="number" min="1" max="10" required>. O PHP ainda precisa conferir o valor?
5. O que filter_var('29 anos', FILTER_VALIDATE_INT) devolve?
6. Por que guardar os erros como $erros['email'] = '…', com o nome do campo na chave?
Missão: a encomenda da padaria aceita de 1 a 10 bolos. A página foi aberta com um envio de 15. Complete a conferência da quantidade com filter_var e FILTER_VALIDATE_INT com min_range 1 e max_range 10; se reprovar, guarde em $erros['qtd'] a mensagem Escolha de 1 a 10 bolos. Depois teste na aba Resultado com 0, 3, 10 e "dois".
<?php
$cliente = '';
$qtd = '';
$erros = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$cliente = trim($_POST['cliente'] ?? '');
$qtd = trim($_POST['qtd'] ?? '');
if ($cliente === '') {
$erros['cliente'] = 'Informe o nome.';
}
// confira a quantidade aqui
if (count($erros) === 0) {
echo "<p>Encomenda de $qtd bolo(s) para " . htmlspecialchars($cliente) . " anotada!</p>";
}
}
?>
<form method="post" novalidate>
<p>Nome: <input name="cliente" value="<?= htmlspecialchars($cliente) ?>">
<?= $erros['cliente'] ?? '' ?></p>
<p>Quantidade: <input name="qtd" value="<?= htmlspecialchars($qtd) ?>">
<?= $erros['qtd'] ?? '' ?></p>
<button>Encomendar</button>
</form>
<?php
$cliente = '';
$qtd = '';
$erros = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$cliente = trim($_POST['cliente'] ?? '');
$qtd = trim($_POST['qtd'] ?? '');
if ($cliente === '') {
$erros['cliente'] = 'Informe o nome.';
}
$n = filter_var($qtd, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10]]);
if ($n === false) {
$erros['qtd'] = 'Escolha de 1 a 10 bolos.';
}
if (count($erros) === 0) {
echo "<p>Encomenda de $qtd bolo(s) para " . htmlspecialchars($cliente) . " anotada!</p>";
}
}
?>
<form method="post" novalidate>
<p>Nome: <input name="cliente" value="<?= htmlspecialchars($cliente) ?>">
<?= $erros['cliente'] ?? '' ?></p>
<p>Quantidade: <input name="qtd" value="<?= htmlspecialchars($qtd) ?>">
<?= $erros['qtd'] ?? '' ?></p>
<button>Encomendar</button>
</form>
Missão: o formulário mostra o erro, mas apaga o que a pessoa digitou. A página foi aberta com um envio (nome certo, e-mail errado). Faça os dois campos voltarem preenchidos, com value="<?= htmlspecialchars(…) ?>".
<?php
$nome = trim($_POST['nome'] ?? '');
$email = trim($_POST['email'] ?? '');
$erros = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST'
&& filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$erros['email'] = 'Este e-mail não parece válido.';
}
?>
<form method="post">
<p>Nome: <input name="nome"></p>
<p>E-mail: <input name="email"> <?= $erros['email'] ?? '' ?></p>
<button>Enviar</button>
</form>
<?php
$nome = trim($_POST['nome'] ?? '');
$email = trim($_POST['email'] ?? '');
$erros = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST'
&& filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$erros['email'] = 'Este e-mail não parece válido.';
}
?>
<form method="post">
<p>Nome: <input name="nome" value="<?= htmlspecialchars($nome) ?>"></p>
<p>E-mail: <input name="email" value="<?= htmlspecialchars($email) ?>"> <?= $erros['email'] ?? '' ?></p>
<button>Enviar</button>
</form>
Resumo
- A validação do navegador é conforto; a do PHP é obrigatória. Tudo o que vem do visitante é suspeito até ser conferido.
- Leia com
?? '', apare comtrim, compare com=== ''para campos obrigatórios. filter_vardevolve o valor aprovado oufalse:FILTER_VALIDATE_EMAIL,FILTER_VALIDATE_INT(commin_range/max_range).- Sempre
=== false: o zero aprovado parece falso numif. - Guarde os problemas em
$erros['campo']; sem erros (count($erros) === 0), siga em frente. - Mostre cada erro ao lado do campo e devolva os valores digitados com
value="<?= htmlspecialchars(…) ?>". - Para escolhas, confira com
in_array(…, true); para texto, garanta que não chegou um array (is_string).