#6 php как узнать, что форму отправили: REQUEST_METHOD, htmlspecialchars и сохранение значений

В уроке про POST мы различали «форму отправили» и «на страницу просто зашли» косвенно — по наличию элементов в $_POST. Разберём правильный приём: проверку метода запроса. Заодно научимся не терять введённые значения после отправки и познакомимся с первой функцией безопасности — htmlspecialchars().

Страница урока: http://localhost:19791/lessons/lesson06-form-sent/

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

PHP собирает служебные сведения о запросе в массив $_SERVER — про него читайте в учебнике: переменные сервера и заголовки HTTP-запроса. Нам нужен элемент $_SERVER['REQUEST_METHOD'] — он содержит метод запроса: 'GET', когда на страницу просто зашли, и 'POST', когда форму отправили. А про превращение спецсимволов HTML в безопасные последовательности читайте там же: кодирование тегов HTML.

Код страницы

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Сравнение чисел: аккуратная форма</title>
</head>
<body>
  <h1>Сравнение чисел</h1>
<?php
$isPosted = $_SERVER['REQUEST_METHOD'] === 'POST';
$a = '';
$b = '';
if (isset($_POST['a'])) {
    $a = $_POST['a'];
}
if (isset($_POST['b'])) {
    $b = $_POST['b'];
}
$isValid = $isPosted && is_numeric($a) && is_numeric($b);
?>
  <form action="" method="post">
    <p>Число A:
      <input type="text" name="a"
             value="<?php echo htmlspecialchars($a); ?>">
    </p>
    <p>Число B:
      <input type="text" name="b"
             value="<?php echo htmlspecialchars($b); ?>">
    </p>
    <p><button type="submit">Сравнить</button></p>
  </form>
<?php
if ($isPosted && !$isValid) {
    echo '<p>Заполните оба поля числами.</p>';
}
if ($isValid) {
    if ($a > $b) {
        echo '<p>' . $a . ' больше ' . $b . '</p>';
    } elseif ($a < $b) {
        echo '<p>' . $a . ' меньше ' . $b . '</p>';
    } else {
        echo '<p>' . $a . ' равно ' . $b . '</p>';
    }
}
?>
</body>
</html>

Что здесь нового:

  • $_SERVER['REQUEST_METHOD'] === 'POST' — форма отправлена именно сейчас; при обычном заходе методом будет 'GET';
  • значения полей подставляются обратно в атрибут value — после отправки форма не очищается, удобно исправить опечатку;
  • htmlspecialchars() экранирует спецсимволы (например, < превращается в &lt;) — текст, введённый пользователем, не сможет «сломать» разметку страницы;
  • is_numeric() проверяет, что в полях числа (документация): это и валидация, и защита — сравнение сработает корректно.

Зачем экранировать: маленький эксперимент

Введите в поле A значение <b>5</b> и отправьте форму: страница ответит «Заполните оба поля числами» — это действительно не число, — а в поле останется ровно тот текст, который вы ввели, вместе с «тегами». А теперь уберите из атрибута value вызов htmlspecialchars() и повторите: разметка поля «поедет», потому что введённый тег станет частью страницы. Запомните правило: всё, что пришло от пользователя и выводится в HTML, сначала прогоняем через htmlspecialchars().

Что произошло на самом деле

Простой заход на страницу — это GET-запрос: REQUEST_METHOD равен 'GET', $_POST пуст. Нажатие кнопки формы — POST-запрос: REQUEST_METHOD равен 'POST', а данные из тела запроса PHP уже разложил в $_POST. Теперь наш скрипт явно различает эти две ситуации, а не гадает по isset().

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

  • отправьте форму с одним пустым полем: увидите подсказку, а заполненное поле сохранит значение;
  • введите в поле слово «семь» — убедитесь, что сообщение о числах сработает;
  • отправьте 3 и 3 — ветка else по-прежнему на месте;
  • посложнее: добавьте третье поле и найдите наибольшее из трёх чисел — теперь с сохранением значений после отправки.

Итоги

  • $_SERVER['REQUEST_METHOD'] точно отвечает на вопрос «отправлена ли форма»;
  • value плюс htmlspecialchars() — поля не очищаются и не ломают разметку;
  • валидация данных (например, is_numeric()) обязательна до любой работы с ними.

В следующем уроке разнесём форму и её обработчик по разным файлам — и научимся после обработки возвращать посетителя на исходную страницу с помощью редиректа.