Validação de formulários em PHP

Na última aula, você aprendeu a receber dados de formulários. Esta aula aborda a etapa mais crítica: a validação. Nunca confie nas entradas do usuário. Os usuários cometem erros de digitação. Os usuários podem ter más intenções. Os usuários deixam campos em branco. A validação de formulários é sua primeira linha de defesa.

1. A arquitetura de validação

Toda a validação ocorre no lado do servidor (PHP). A validação no lado do cliente (HTML5 / JavaScript) é apenas uma camada de conveniência opcional:

TEXT
Browser (JS validation, easily bypassed)
      ↓
PHP server (MUST validate — never skip this step)
      ↓
Store in database
PHP
<?php
$errors = [];

// 1. Collect and clean input
$name  = trim($_POST['name'] ?? '');
$age   = $_POST['age'] ?? '';
$email = trim($_POST['email'] ?? '');

// 2. Apply validation rules
if ($name === '') {
    $errors['name'] = 'Name is required';
} elseif (strlen($name) > 50) {
    $errors['name'] = 'Name must not exceed 50 characters';
}

if ($age === '') {
    $errors['age'] = 'Age is required';
} elseif (!is_numeric($age)) {
    $errors['age'] = 'Age must be a number';
} elseif ((int)$age < 1 || (int)$age > 150) {
    $errors['age'] = 'Age must be between 1 and 150';
}

if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors['email'] = 'Invalid email format';
}  // Email is optional, but if provided it must be valid

// 3. Determine the outcome
if (empty($errors)) {
    echo "Validation passed!";
} else {
    foreach ($errors as $field => $msg) {
        echo "<p style='color:red'>{$msg}</p>";
    }
}
?>
💡 Dica: Use um array associativo $errors para coletar mensagens de erro. A estrutura $errors['field_name'] facilita a exibição dos erros ao lado dos campos correspondentes.


2. Validação com filter_var()

A função embutida filter_var() do PHP é a maneira mais simples de validar tipos de dados comuns:

PHP
<?php
// Email validation
$email = "test@example.com";
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
// "test@example.com" (returns the original value on success)

$badEmail = "not-an-email";
var_dump(filter_var($badEmail, FILTER_VALIDATE_EMAIL));
// bool(false) (validation failed)

// URL validation
$url = "https://www.example.com";
var_dump(filter_var($url, FILTER_VALIDATE_URL));

// IP address validation
$ip = "192.168.1.1";
var_dump(filter_var($ip, FILTER_VALIDATE_IP));

// Integer range validation
$age = 25;
var_dump(filter_var($age, FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 150]
]));
?>

▶ Exemplo: Validando um formulário inteiro com filter_var

PHP
<?php
$errors = [];

// Validate email
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
if ($email === false) {
    $errors['email'] = 'Please enter a valid email address';
}

// Validate URL
$website = filter_var($_POST['website'] ?? '', FILTER_VALIDATE_URL);
if ($website === false && ($_POST['website'] ?? '') !== '') {
    $errors['website'] = 'Please enter a valid URL';
}

// Validate integer range
$age = filter_var($_POST['age'] ?? '', FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 120]
]);
if ($age === false) {
    $errors['age'] = 'Age must be between 1 and 120';
}
?>
▶ Experimente

3. Validação de expressões regulares com preg_match()

Quando os validadores integrados não são suficientes, entram em cena as expressões regulares:

PHP
<?php
// Phone number (10-digit North American)
$phone = "5551234567";
if (preg_match('/^\d{10}$/', $phone)) {
    echo "Phone number format is valid";
}

// Username (alphanumeric + underscore, 3-20 characters)
$username = "user_2024";
if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
    echo "Username format is valid";
}

// Strong password (min 8 chars, at least one lowercase, one uppercase, one digit)
$password = "MyPass123";
if (preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
    echo "Password meets strength requirements";
}
?>

4. Fortalecimento da segurança

(1) htmlspecialchars() — Prevenção de ataques XSS

Se uma entrada do usuário que contenha as tags <script> for exibida diretamente, o navegador irá executá-la:

PHP
<?php
$userInput = '<script>alert("Hacked!")</script>';

// ❌ Dangerous — direct output
echo $userInput;  // The script executes!

// ✅ Safe — escape special characters
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Renders as: &lt;script&gt;alert(&quot;Hacked!&quot;)&lt;/script&gt;
// The browser displays the text rather than executing it
?>

(2) strip_tags() — Remoção de tags HTML

PHP
<?php
$content = "<p>This is <b>important</b> content</p>";
echo strip_tags($content);  // This is important content

// Optionally allow specific tags (whitelist as second argument)
echo strip_tags($content, '<b><i>');  // This is <b>important</b> content
?>

(3) trim() — Remoção de espaços em branco no início e no final

PHP
<?php
$input = "   John   ";
echo trim($input);  // John
?>

▶ Exemplo: Uma função completa de sanitização de entradas

PHP
<?php
/**
 * Clean user-supplied text input
 */
function cleanInput(string $data): string {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
    return $data;
}

$name  = cleanInput($_POST['name'] ?? '');
$comment = cleanInput($_POST['comment'] ?? '');
?>
▶ Experimente

5. Juntando tudo

▶ Exemplo: Formulário de inscrição completo com validação

PHP
<?php
$errors = [];
$username = $email = $password = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $email    = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    // Username validation
    if ($username === '') {
        $errors['username'] = 'Username is required';
    } elseif (strlen($username) < 3 || strlen($username) > 20) {
        $errors['username'] = 'Username must be 3–20 characters';
    } elseif (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
        $errors['username'] = 'Username may only contain letters, digits, and underscores';
    }

    // Email validation
    $emailClean = filter_var($email, FILTER_VALIDATE_EMAIL);
    if ($email === '') {
        $errors['email'] = 'Email is required';
    } elseif ($emailClean === false) {
        $errors['email'] = 'Invalid email format';
    }

    // Password validation
    if ($password === '') {
        $errors['password'] = 'Password is required';
    } elseif (strlen($password) < 6) {
        $errors['password'] = 'Password must be at least 6 characters';
    }

    if (empty($errors)) {
        $usernameSafe = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
        echo "<h3 style='color:green'>Registration successful! Welcome, {$usernameSafe}!</h3>";
        $username = $email = $password = '';
    }
}
?>

<form method="POST" action="">
    <div>
        <label>Username:</label>
        <input type="text" name="username"
               value="<?= htmlspecialchars($username) ?>">
        <span style="color:red"><?= $errors['username'] ?? '' ?></span>
    </div>
    <div>
        <label>Email:</label>
        <input type="email" name="email"
               value="<?= htmlspecialchars($email) ?>">
        <span style="color:red"><?= $errors['email'] ?? '' ?></span>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password">
        <span style="color:red"><?= $errors['password'] ?? '' ?></span>
    </div>
    <button type="submit">Register</button>
</form>
▶ Experimente

❓ Perguntas Frequentes

P: Eu já tenho os atributos required e pattern no front-end. Ainda preciso da validação em PHP? R: Com certeza. A validação no front-end é facilmente contornada — basta desativar o JavaScript ou simular uma solicitação com o curl. A validação no PHP é a última linha de defesa e não pode ser ignorada.

P: Quando devo usar htmlspecialchars e quando strip_tags? R: Use htmlspecialchars quando quiser exibir a entrada do usuário tal como está, mas com segurança (ele faz o escape do HTML sem removê-lo). Use strip_tags quando quiser remover completamente todo o HTML. Na maioria dos casos, htmlspecialchars é suficiente.

P: As expressões regulares parecem intimidadoras. Preciso memorizá-las? R: Não. Padrões comuns (e-mail, telefone, URL) têm fórmulas bem conhecidas. Basta lembrar-se de preg_match() e de alguns símbolos básicos — ^ (início), $ (fim), \d (dígito), [] (conjunto de caracteres), {} (quantidade) — e você vai se virar bem.

📖 Resumo

📝 Exercícios

  1. Crie um formulário de edição de perfil com apelido, idade, e-mail e URL do site pessoal. Valide cada campo com a regra apropriada.
  2. Crie um formulário para alteração de senha: solicite a senha atual, exija que a nova senha tenha pelo menos 8 caracteres, com uma combinação de letras e números, e confirme se as duas senhas coincidem.
  3. Teste uma vulnerabilidade XSS: crie um formulário de livro de visitas que omita deliberadamente htmlspecialchars. Tente postar <script>alert(1)</script> e observe o que acontece. Em seguida, adicione htmlspecialchars e teste novamente.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%