Форма с вложением требует больше проверок, чем обычная отправка имени и текста. Нельзя доверять имени файла, MIME-типу из браузера или расширению: загруженный файл должен пройти серверную проверку до сохранения или отправки.

HTML-форма
<form method="post" enctype="multipart/form-data">
<input type="text" name="name" required>
<input type="email" name="email" required>
<input type="file" name="attachment" required>
<button type="submit">Отправить</button>
</form>Проверяем ошибку и размер
$file = $_FILES['attachment'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException('Ошибка загрузки файла');
}
if ($file['size'] > 5 * 1024 * 1024) {
throw new RuntimeException('Файл слишком большой');
}Определяем MIME на сервере
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = [
'application/pdf' => 'pdf',
'image/jpeg' => 'jpg',
'image/png' => 'png',
];
if (!isset($allowed[$mime])) {
throw new RuntimeException('Недопустимый тип файла');
}Сохраняем под новым именем
$name = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
$target = __DIR__ . '/private_uploads/' . $name;
if (!move_uploaded_file($file['tmp_name'], $target)) {
throw new RuntimeException('Не удалось сохранить файл');
}move_uploaded_file() дополнительно проверяет, что исходный файл действительно пришёл через механизм HTTP POST PHP. Каталог вложений лучше держать вне публичного web-root либо запрещать в нём выполнение скриптов.
Отправка письма
Для вложений лучше использовать поддерживаемую почтовую библиотеку, например Symfony Mailer или PHPMailer, а не собирать MIME-письмо вручную через mail(). SMTP-аутентификацию и пароль храните в переменных окружения.
Что ещё обязательно
- CSRF-защита;
- ограничение частоты отправок;
- серверная валидация email;
- лимит разрешённых типов;
- удаление временных файлов после отправки;
- не использовать исходное имя файла как путь на сервере.
