#7 php форма и обработчик в разных файлах: header Location и редирект после POST

До сих пор форма и код её обработки жили в одном файле — с пустым action="" мы работали в уроках про форму с GET, форму с POST и проверку отправки формы.

Теперь разнесём их по разным файлам: страница с формой останется «витриной», а приём данных займётся отдельный обработчик. И сразу научимся главному приёму такого разделения: после обработки отправлять посетителя обратно — перенаправлением (редиректом).

Немного теории

Перенаправление — это обычный ответ сервера, только со специальным статусом (3xx) и заголовком Location: браузер, получив такой ответ, сам тут же запрашивает адрес из заголовка. Как отправлять заголовки из PHP — читайте в учебнике: перенаправление.

Файл первый: страница с формой

Пример сменим: пусть будет форма «оставить сообщение» с полем для имени и большим текстовым полем (многострочное поле — textarea):

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Форма и обработчик</title>
</head>
<body>
  <h1>Оставить сообщение</h1>
<?php if (isset($_GET['sent'])) { ?>
  <p><strong>Спасибо! Сообщение получено.</strong></p>
<?php } ?>
  <form action="handler.php" method="post">
    <p>Имя:
      <input type="text" name="name">
    </p>
    <p>Сообщение:<br>
      <textarea name="text" rows="5" cols="40"></textarea>
    </p>
    <p><button type="submit">Отправить</button></p>
  </form>
</body>
</html>

Обратите внимание: в action теперь адрес другого файла — handler.php. А блок «Спасибо» наверху показывается только если в адресе есть параметр sent — откуда он возьмётся, увидите через минуту.

Файл второй: обработчик

handler.php — это чистый PHP-файл без единой строки HTML:

<?php
// handler.php - принимает форму и возвращает на страницу
$isPosted = $_SERVER['REQUEST_METHOD'] === 'POST';
$name = '';
$text = '';
if (isset($_POST['name'])) {
    $name = $_POST['name'];
}
if (isset($_POST['text'])) {
    $text = $_POST['text'];
}
if (!$isPosted || $name === '' || $text === '') {
    // данных нет или они пустые - просто возвращаемся к форме
    header('Location: index.php');
    exit;
}

// здесь данные валидны: в реальном приложении их
// сохранили бы в базу или отправили на почту
header('Location: index.php?sent=1');
exit;

Как это работает

  1. браузер отправляет данные формы POST-запросом на handler.php;
  2. обработчик проверяет данные (метод запроса и заполненность полей — всё это мы разбирали в прошлом уроке);
  3. если всё хорошо — «делает работу» (у нас это место с комментарием) и отвечает редиректом: header('Location: index.php?sent=1');
  4. браузер автоматически запрашивает index.php?sent=1 уже обычным GET-запросом;
  5. страница с формой видит параметр sent и печатает «Спасибо».

Посмотрите на адресную строку после отправки: там index.php?sent=1 — данные уехали на сервер POST-ом, а «квитанция» вернулась GET-параметром.

Почему редирект, а не просто «Спасибо» в обработчике

Можно было бы напечатать «Спасибо» прямо в handler.php — но тогда посетитель останется на странице обработчика, и: во-первых, её обновление (F5) снова отправит форму — с тем самым предупреждением браузера из урока про POST; во-вторых, адрес обработчика останется в истории. После редиректа обновляется уже обычная GET-страница, и ничего не отправляется повторно. Этот приём называют POST-Redirect-GET.

И два обязательных правила работы с header() (документация): заголовки отправляются до любого вывода в браузер — поэтому в handler.php нет ни HTML, ни echo; и сразу после header() вызывайте exit, иначе скрипт продолжит выполняться дальше.

Попробуйте сами

  • отправьте сообщение и нажмите F5 — переспроса о повторной отправке не будет;
  • откройте handler.php напрямую, без формы, — вас просто вернёт на страницу с формой (подумайте, почему);
  • в DevTools (F12, Network) отправьте форму и найдите три запроса подряд: POST на handler.php, ответ 302, затем GET на index.php;
  • доработка: при пустых полях показывайте на странице формы сообщение «заполните оба поля» — подсказка: передавайте ещё один GET-параметр при редиректе, а показ сообщения делайте по образцу урока 6.

Итоги

  • форму и обработчик можно (и часто удобно) держать в разных файлах: action="handler.php";
  • после обработки POST-данных правильно возвращать посетителя редиректом: header('Location: ...') + exit;
  • header() работает только до любого вывода — обработчик не должен ничего печатать;
  • паттерн POST-Redirect-GET избавляет от повторной отправки формы при обновлении страницы.

В следующем уроке начинается новая тема: научим сервер «узнавать» посетителя при повторном заходе — куки.