PHP-форма отправляется обратно в скрипт, который её вывел: пишите <form method="post"> с именованными полями, а когда $_SERVER['REQUEST_METHOD'] равен 'POST', читайте каждое поле как $_POST['name']. Всё, что выводите обратно, экранируйте через htmlspecialchars().
Запустите этот блок, введите в форму имя и нажмите Send: тот же скрипт выполнится снова, на этот раз с заполненным $_POST.
Здесь происходят три вещи, которые нужны каждому обработчику формы:
- Проверка метода. При первом визите запрос это GET, и обрабатывать нечего.
?? 'GET'важен только тогда, когда файл запускается из командной строки, гдеREQUEST_METHODне задан; на веб-сервере он задан всегда. ?? ''для каждого поля. Поля может не быть (переименованный input, запрос, отправленный скриптом), а чтение отсутствующего ключа печатает предупреждение.- Экранирование при выводе. Имя возвращается на страницу дважды, и
htmlspecialchars()гарантирует, что значение вроде<b>Ada</b>показывается как текст, а не становится разметкой. Подробности на странице про htmlspecialchars.
Проверка формы и ошибки рядом с полями
Собирайте ошибки в массив с ключами по именам полей. Если массив пуст, форма корректна; иначе выведите форму снова с каждым сообщением под своим полем, сохранив введённые посетителем данные. Отправьте её пустой, потом с неверным email, потом с корректными значениями.
novalidate отключает собственные проверки браузера, чтобы вы видели, как работают проверки PHP. В реальной форме оставляйте обе: проверка в браузере это удобство для посетителя, а проверка в PHP это та, которая действительно имеет значение, потому что любой может отправить запрос без вашей формы. filter_var() проверяет email и диапазон целого числа одним вызовом каждый; другие фильтры описаны на странице про filter_var.
Чекбоксы, радиокнопки и select
Текстовое поле отправляет значение всегда, даже пустое. Остальные элементы нет:
- Неотмеченный чекбокс не отправляет вообще ничего, поэтому его ключа в
$_POSTнет. - Чекбоксы с именем
topics[]приходят массивом:$_POST['topics']это['php', 'sql']. - Группа радиокнопок или
<select>отправляетvalueвыбранного варианта.
Чтобы сохранить выбор после отправки, выводите checked или selected у совпавшего варианта. Отметьте несколько чекбоксов, выберите уровень и нажмите Save.
Две проверки после чтения ввода важнее, чем кажется. array_intersect() отбрасывает любую тему, которой не было в вашем списке, а проверка is_string() вместе с array_key_exists() возвращает уровню значение по умолчанию, если кто-то отправит значение, которого вы не предлагали (или массив, из-за которого $levels[$level] выбросил бы ошибку). Select ограничивает то, что может отправить браузер, но не то, что может содержать запрос.
Многошаговые формы со скрытыми полями
Скрытое поле переносит значение с одного шага на следующий, не показывая его. Здесь шаг 1 спрашивает имя, шаг 2 спрашивает язык и передаёт имя дальше в скрытом поле, а шаг 3 показывает оба. Пройдите все три шага.
Скрытые поля видны в исходном коде страницы и редактируются, как любое другое поле, поэтому они годятся, чтобы переносить между шагами собственные ответы посетителя, но никогда не для цены, ID пользователя или роли. Храните такое на сервере, в сессии; смотрите сессии и cookie.
Post/Redirect/Get: не отправлять форму повторно при обновлении
Если после успешного POST скрипт сразу выводит страницу "спасибо", то при нажатии обновления браузер отправит POST снова, и заказ или комментарий сохранится дважды. Решение: сохранить, сделать редирект и показать результат по 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 говорит браузеру выполнить редирект через GET. exit после header() важен: без него остаток скрипта продолжает выполняться. Кроме того, редирект должен произойти до любого вывода, иначе PHP откажет с ошибкой "headers already sent".
В реальных формах добавляют ещё CSRF-токен: случайное значение, которое хранится в сессии и в скрытом поле и при отправке сравнивается через hash_equals(), чтобы другой сайт не мог отправить данные в вашу форму от имени вошедшего посетителя.
Проверка валидации без браузера
Поместите правила в функцию, которая принимает массив и возвращает ошибки. Страница вызывает её с $_POST, а вы можете вызвать её с любым массивом, и тогда пограничные случаи легко проверить. Добавьте в список свои случаи.
Четвёртый случай обычно забывают. Запрос может отправить name[]=x, и тогда $_POST['name'] станет массивом, а передача массива в trim() выбрасывает TypeError и останавливает скрипт. Предварительная проверка is_string() превращает враждебный ввод в обычную ошибку валидации. Приведение (string) в предыдущих блоках тоже избегает TypeError, но превращает массив в слово Array и печатает предупреждение, поэтому is_string() более чистая проверка.
Частая ошибка: чтение $_POST до отправки формы
Самый частый первый скрипт формы читает поля в начале файла:
<?php
$name = $_POST['name'];
echo "Hello, $name";
При первом визите ничего не отправлено, поэтому PHP печатает:
Warning: Undefined array key "name" in /var/www/form.php on line 2
Решение это шаблон из первого блока: обрабатывайте форму, только когда запрос это POST, и читайте каждое поле со значением по умолчанию, $_POST['name'] ?? ''. Если предупреждение появляется после отправки, сравните атрибут name в HTML с ключом в PHP: name="Name" и $_POST['name'] это разные ключи.
Часто задаваемые вопросы
Как отправить PHP-форму на ту же страницу?
Пишите <form method="post"> без action (или с action=""). Браузер отправит данные на текущий URL, поэтому один и тот же скрипт показывает форму и обрабатывает её: проверьте $_SERVER['REQUEST_METHOD'] === 'POST' и читайте поля из $_POST.
Как сохранить значения формы после отправки в PHP?
Выводите отправленное значение обратно в поле, экранировав его: <input name="email" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">. Для чекбокса выводите checked, а для пункта select selected, когда отправленное значение совпадает.
Как получить значения нескольких чекбоксов в PHP?
Дайте чекбоксам одинаковое имя со скобками в конце, name="topics[]". Тогда PHP сложит отмеченные значения в массив $_POST['topics']. Неотмеченные чекбоксы ничего не отправляют, поэтому используйте $_POST['topics'] ?? [] и проверяйте каждое значение по списку, который вы предлагали.
Почему обновление страницы повторно отправляет PHP-форму?
Последний запрос был POST, поэтому при обновлении браузер повторяет его. После успешной отправки сделайте редирект через header('Location: /thanks.php'); exit; (шаблон Post/Redirect/Get). Тогда обновление повторит безобидный GET.
Безопасно ли использовать $_POST в PHP?
Нет, каждое значение в $_POST приходит от посетителя и может быть чем угодно, в том числе HTML, SQL или массивом там, где вы ждали строку. Проверяйте его на входе (filter_var, длина и список допустимых значений) и экранируйте на выходе (htmlspecialchars для HTML, подготовленные запросы для SQL).