Перейти к основному содержимому

От HTML-формы до записи в базу данных на PHP

Разработчику Архитектору

Один сквозной сценарий связывает темы форм, PDO и исключений. Без фреймворка — чтобы было видно каждый шаг. Загрузка файлов в форме — отдельно: Загрузка файлов и валидация в PHP.

Этот материал удобно воспринимать как шаблон "минимального production-ready потока" — принять вход, провалидировать, безопасно записать, корректно вернуть пользователя в UI.


Схема потока

Разбор:

  • Диаграмма показывает полный путь запроса от браузера до базы данных и обратно.
  • Ветвление alt/else разделяет два сценария: валидация с ошибками и успешная запись.
  • В успешной ветке сервер отправляет 302 Redirect, чтобы браузер перешел на отдельную страницу результата.
  • Такой паттерн устраняет повторную отправку формы при обновлении страницы.
  • Блок помогает визуально связать отдельные фрагменты PHP-кода в единую цепочку обработки.

Таблица и подключение

CREATE TABLE subscribers (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL,
UNIQUE KEY uq_email (email)
);

Разбор:

  • id — автоинкрементный первичный ключ для уникальной идентификации записи.
  • Поля name и email объявлены как NOT NULL, поэтому пустое значение база не примет.
  • created_at хранит момент создания подписки для аудита и аналитики.
  • UNIQUE KEY uq_email (email) запрещает дублирование одной и той же почты.
  • Ограничение уникальности дополняет серверную валидацию и защищает данные от гонок.

Файл bootstrap.php — общее подключение PDO (подключается из скриптов):

<?php
declare(strict_types=1);

$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4',
getenv('DB_USER') ?: 'app_user',
getenv('DB_PASS') ?: '',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
],
);

Разбор:

  • Файл служит единым bootstrap-слоем для подключения к базе из разных скриптов.
  • getenv('DB_USER') и getenv('DB_PASS') читают секреты из окружения, а не из репозитория.
  • DSN содержит charset=utf8mb4, чтобы корректно хранить многоязычный пользовательский ввод.
  • ERRMODE_EXCEPTION делает обработку ошибок централизованной через исключения.
  • Подключение выносится отдельно, чтобы сократить дублирование кода в обработчиках форм.

Шаг 1 — форма (GET)

public/register.php:

Код ITЗагрузка примера кода…

Разбор:

  • Скрипт отвечает за страницу формы по GET и отрисовывает HTML для ввода данных.
  • Условие if (!empty($_GET['error'])) показывает сообщение, если предыдущая отправка завершилась ошибкой.
  • <form action="register_submit.php" method="post"> отправляет данные в отдельный обработчик.
  • Атрибуты required, maxlength и type="email" дают базовую клиентскую валидацию в браузере.
  • Финальная проверка все равно выполняется на сервере, потому что клиентские ограничения можно обойти.

method="post" — данные в теле запроса, не в URL. Пароли и персональные данные не передают через GET.


Шаг 2 — приём и валидация (POST)

public/register_submit.php:

Код ITЗагрузка примера кода…

Разбор:

  • require .../bootstrap.php подключает готовый объект PDO и общие настройки окружения.
  • $_POST['name'] ?? '' и trim(...) безопасно читают ввод и убирают лишние пробелы по краям.
  • Массив $errors накапливает все нарушения правил вместо остановки на первой ошибке.
  • mb_strlen($name) корректно считает длину Unicode-строк, включая кириллицу.
  • filter_var(..., FILTER_VALIDATE_EMAIL) проверяет формат адреса на сервере.
  • При наличии ошибок обработчик делает редирект и завершает выполнение через exit.

Фильтрация на сервере обязательна: клиентская проверка в браузере обходится за секунду.

Дополнительный практический совет — если форма длиннее двух-трех полей, ошибки и старые значения лучше хранить в сессии и возвращать через редирект, чтобы пользователь не терял введенные данные.


Шаг 3 — запись через PDO

Код ITЗагрузка примера кода…

Разбор:

  • Блок try оборачивает операцию записи в БД и последующий контроль ошибок.
  • prepare + execute выполняют вставку подписчика безопасно и с параметризацией.
  • DateTimeImmutable('now') формирует стабильный timestamp без изменения исходного объекта времени.
  • В catch код 1062 обрабатывает нарушение уникальности email отдельным пользовательским сценарием.
  • Для прочих ошибок пишется серверный лог и возвращается 500, чтобы не раскрывать внутренние детали.
  • Финальный редирект на thanks.php реализует паттерн Post/Redirect/Get.

Паттерн Post/Redirect/Get: после успешного POST браузер получает редирект на thanks.php. Обновление страницы не повторяет INSERT.

Если операция состоит из нескольких SQL-команд (например, подписчик + аудит-лог), объединяйте их в транзакцию из статьи про PDO.


Шаг 4 — страница благодарности

public/thanks.php — статичный HTML "Спасибо за подписку". Без повторной обработки POST.


Безопасность в этом сценарии

УгрозаМера
SQL-инъекцияТолько prepare + именованные параметры
XSS при выводе ошибокhtmlspecialchars($msg, ENT_QUOTES, 'UTF-8') для любого текста из POST
Повторная отправка формыРедирект после успешного POST
CSRFТокен в сессии и скрытое поле формы (см. сессии)
Перебор emailRate limit на уровне веб-сервера или кэша

Развитие сценария

  1. Вынести валидацию в класс App\Validation\SubscribeValidatorпространства имён.
  2. Хранить ошибки в $_SESSION и показывать их рядом с полями — сессии.
  3. Статус подписки задать через enum.
  4. Перейти на маршрутизацию фреймворка — Laravel или Symfony.

Что изучить дальше