От 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 | Токен в сессии и скрытое поле формы (см. сессии) |
| Перебор email | Rate limit на уровне веб-сервера или кэша |
Развитие сценария
- Вынести валидацию в класс
App\Validation\SubscribeValidator— пространства имён. - Хранить ошибки в
$_SESSIONи показывать их рядом с полями — сессии. - Статус подписки задать через enum.
- Перейти на маршрутизацию фреймворка — Laravel или Symfony.
Что изучить дальше
- Работа с данными со страницы
- PDO
- Работа с базами данных из PHP
- Чек-лист самопроверки
- Обработка исключений в прикладном коде