W3docs

Валидация форм в PHP

Валидация форм в PHP — проверка данных на сервере перед обработкой. Защита от XSS и SQL-инъекций с помощью htmlspecialchars и подготовленных запросов.

Валидация форм — это процесс проверки того, что данные, отправленные пользователем через HTML-форму, присутствуют, правильно отформатированы и безопасны для использования до того, как скрипт начнёт их обрабатывать. В PHP валидация выполняется на сервере после отправки формы, поэтому её нельзя обойти, отключив JavaScript или отправив запрос напрямую.

В этой главе рассматривается полный процесс валидации: почему важна серверная проверка, в чём разница между санитизацией и валидацией, какие PHP-функции для этого используются, а также полный безопасный пример, который можно адаптировать под свои нужды. Если вы только начинаете работать с вводом данных форм, сначала прочитайте Обработка форм в PHP.

Почему важна серверная валидация

Клиентская валидация (HTML5 required, type="email", JavaScript) улучшает пользовательский опыт, мгновенно выявляя ошибки — но это лишь вспомогательный инструмент. Целеустремлённый или злонамеренный пользователь может отключить JavaScript или отправить специально сформированный HTTP-запрос, минуя браузер полностью. Сервер — единственное место, где валидации можно доверять. Серверная валидация на PHP позволяет:

  • Гарантировать, что обязательные поля действительно присутствуют и не пусты.
  • Убедиться, что значения соответствуют ожидаемому формату (корректный email, число в допустимом диапазоне).
  • Нейтрализовать опасные данные до того, как они попадут в базу данных или на HTML-страницу, предотвращая SQL-инъекции и межсайтовый скриптинг (XSS).

Считайте каждое значение в $_POST, $_GET и $_REQUEST ненадёжным до тех пор, пока не проверите его. Эти массивы поступают из суперглобальных переменных PHP.

Санитизация и валидация

Эти два понятия часто путают, но они выполняют разные функции:

  • Санитизация очищает значение — она удаляет или экранирует нежелательные символы. trim() убирает пробелы в начале и конце строки; htmlspecialchars() преобразует <, >, & и кавычки в HTML-сущности, чтобы они отображались как текст, а не выполнялись.
  • Валидация проверяет значение по правилу и сообщает, прошло оно или нет — при этом значение не изменяется. filter_var($email, FILTER_VALIDATE_EMAIL) возвращает email, если он выглядит корректно, или false, если нет.

Типичный порядок действий: сначала санитизация (обрезка пробелов), затем валидация, и наконец экранирование при выводе (когда значение вставляется обратно в HTML). Подробнее о системе фильтрации PHP читайте в PHP Filters и filter_var().

Шаги валидации

Процедура серверной валидации обычно состоит из одних и тех же четырёх шагов:

  1. Создать HTML-форму и указать в атрибуте action PHP-скрипт.
  2. Определить отправку методом POST с помощью $_SERVER["REQUEST_METHOD"].
  3. Для каждого поля: считать значение, санитизировать его, затем проверить — собирая сообщения об ошибках.
  4. Если все поля прошли проверку — обработать данные; в противном случае — снова отобразить форму с сообщениями об ошибках и значениями, которые пользователь уже ввёл.

Полезные PHP-функции для валидации

ФункцияНазначение
trim()Удаляет пробельные символы в начале и конце строки.
empty()Проверяет, отсутствует ли значение или является ли оно пустой строкой.
filter_var()Валидирует или санитизирует значение с помощью встроенного фильтра (например, FILTER_VALIDATE_EMAIL, FILTER_VALIDATE_INT).
is_numeric()Возвращает true, если значение является числом или числовой строкой.
htmlspecialchars()Экранирует специальные HTML-символы для предотвращения XSS при выводе.
preg_match()Проверяет значение по заданному регулярному выражению.

Минимальный пример

Прежде чем перейти к полному примеру с базой данных, рассмотрим простейшую валидацию, демонстрирующую основной паттерн — санитизация, затем валидация, затем сбор ошибок:

<?php
$email = trim($_POST["email"] ?? "");
$errors = [];

if ($email === "") {
    $errors["email"] = "Please enter your email.";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors["email"] = "Please enter a valid email address.";
}

if (empty($errors)) {
    echo "Valid email: " . htmlspecialchars($email);
} else {
    echo $errors["email"];
}
?>

Если $_POST["email"] содержит " [email protected] ", окружающие пробелы обрезаются, значение проходит валидацию, и скрипт выводит Valid email: [email protected]. Строка вроде "not-an-email" приведёт к выводу Please enter a valid email address.

Полный пример: валидация и сохранение данных формы

Пример ниже проверяет имя, email, пол и комментарий, а затем вставляет строку с помощью подготовленного запроса — именно этот подход (с использованием mysqli) защищает от SQL-инъекций, поскольку пользовательские данные передаются отдельно от SQL-текста и никогда не интерпретируются как код.

Обновите учётные данные базы данных и убедитесь, что в базе данных существует таблица users со столбцами name, email, gender и comment, прежде чем запускать этот пример.

<?php
// Database connection setup
$link = mysqli_connect("localhost", "username", "password", "database");
if (!$link) {
    die("Connection failed: " . mysqli_connect_error());
}

// Define variables and initialize with empty values
$name = $email = $gender = $comment = $website = "";
$name_err = $email_err = $gender_err = $comment_err = "";
 
// Processing form data when form is submitted
if($_SERVER["REQUEST_METHOD"] == "POST"){
    // Validate name
    if(empty(trim($_POST["name"] ?? ""))){
        $name_err = "Please enter your name.";
    } else{
        $name = trim($_POST["name"]);
    }
    
    // Validate email
    if(empty(trim($_POST["email"] ?? ""))){
        $email_err = "Please enter your email.";
    } elseif(!filter_var(trim($_POST["email"] ?? ""), FILTER_VALIDATE_EMAIL)) {
        $email_err = "Please enter a valid email address.";
    } else{
        $email = trim($_POST["email"]);
    }
    
    // Validate gender
    if(!isset($_POST["gender"]) || empty($_POST["gender"])){
        $gender_err = "Please select your gender.";
    } else{
        $gender = $_POST["gender"];
    }
    
    // Validate comment
    if(empty(trim($_POST["comment"] ?? ""))){
        $comment_err = "Please enter your comment.";
    } else{
        $comment = trim($_POST["comment"]);
    }
    
    // Check input errors before inserting in database
    if(empty($name_err) && empty($email_err) && empty($gender_err) && empty($comment_err)){
        // Prepare an insert statement
        $sql = "INSERT INTO users (name, email, gender, comment) VALUES (?, ?, ?, ?)";
         
        if($stmt = mysqli_prepare($link, $sql)){
            // Bind variables to the prepared statement as parameters
            mysqli_stmt_bind_param($stmt, "ssss", $param_name, $param_email, $param_gender, $param_comment);
            
            // Set parameters
            $param_name = $name;
            $param_email = $email;
            $param_gender = $gender;
            $param_comment = $comment;
            
            // Attempt to execute the prepared statement
            if(mysqli_stmt_execute($stmt)){
                // Records created successfully. Redirect to landing page
                header("location: index.php");
                exit();
            } else{
                echo "Something went wrong. Please try again later.";
            }
        }
         
        // Close statement
        mysqli_stmt_close($stmt);
    }
}

// Close connection
mysqli_close($link);
?>
<!DOCTYPE html>
<html>
<head><title>PHP Form Validation</title></head>
<body>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
    Name: <input type="text" name="name" value="<?php echo htmlspecialchars($name); ?>">
    <span class="error"><?php echo $name_err; ?></span><br><br>
    Email: <input type="text" name="email" value="<?php echo htmlspecialchars($email); ?>">
    <span class="error"><?php echo $email_err; ?></span><br><br>
    Gender:
    <input type="radio" name="gender" value="female"> Female
    <input type="radio" name="gender" value="male"> Male
    <span class="error"><?php echo $gender_err; ?></span><br><br>
    Comment: <textarea name="comment"><?php echo htmlspecialchars($comment); ?></textarea>
    <span class="error"><?php echo $comment_err; ?></span><br><br>
    <input type="submit" value="Submit">
</form>
</body>
</html>

Отображение ошибок и сохранение введённых данных

Обратите внимание на три детали в примере, которые обеспечивают хороший UX формы:

  • Каждая переменная с ошибкой ($name_err, $email_err, …) выводится в теге <span class="error"> рядом с соответствующим полем, чтобы пользователь точно знал, что нужно исправить.
  • В атрибуте action формы используется htmlspecialchars($_SERVER["PHP_SELF"]). Без экранирования PHP_SELF может быть использован для внедрения скриптов через URL, поэтому экранирование здесь — мера безопасности, а не просто форматирование.
  • Каждое значение, повторно отображаемое в форме, передаётся через htmlspecialchars(). Это одновременно предотвращает XSS и позволяет пользователю сохранить уже введённые данные, не перепечатывая всё заново из-за одного неверного поля.

Распространённые ошибки

  • empty() считает "0" пустым значением. Поле, содержащее строку "0", не пройдёт проверку empty(). Для полей, где 0 является допустимым значением, используйте явное сравнение: if ($value === "").
  • Проверяйте тип до того, как доверять ему. Значения в $_POST всегда являются строками. Используйте filter_var($n, FILTER_VALIDATE_INT) или is_numeric() вместо того, чтобы предполагать, что пришло число.
  • Экранируйте при выводе, а не при сохранении. Храните в базе данных исходное (прошедшее валидацию) значение и применяйте htmlspecialchars() только при вставке в HTML. Экранирование перед сохранением испортит данные для не-HTML-использования.
  • Используйте оператор объединения с null. $_POST["x"] ?? "" позволяет избежать предупреждений «Undefined array key», когда поле отсутствует.

Валидация других типов полей

Тот же паттерн применяется к любому полю — меняется только правило валидации:

<?php
// Age: an integer between 18 and 120
$age = filter_var($_POST["age"] ?? "", FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 18, "max_range" => 120],
]);
if ($age === false) {
    echo "Please enter an age between 18 and 120.";
}

// Username: 3-16 letters, digits, or underscores
$username = trim($_POST["username"] ?? "");
if (!preg_match('/^[A-Za-z0-9_]{3,16}$/', $username)) {
    echo "Username must be 3-16 letters, digits, or underscores.";
}
?>

Для правил, специфичных для URL и email, смотрите PHP Form URL & E-mail; для обязательных полей — PHP Form Required Fields; а чтобы увидеть все части собранными вместе — PHP Complete Form.

Заключение

Серверная валидация в PHP делает форму надёжной: она подтверждает наличие и правильность формата данных, а в сочетании с htmlspecialchars() при выводе и подготовленными запросами при сохранении — защищает от XSS и SQL-инъекций. Основной рецепт всегда одинаков: санитизация, валидация, сбор ошибок и повторное отображение формы с понятными сообщениями при наличии проблем. Освоив этот цикл, вы сможете проверять любое поле, просто подставив другое правило.

Практика

Практика
Какие из следующих утверждений относятся к ключевым элементам валидации форм в PHP?
Какие из следующих утверждений относятся к ключевым элементам валидации форм в PHP?
Was this page helpful?