March 24, 2023

Вменяемая инструкция к PHPMailer. Отправка писем и файлов на почту.

PHPMailer - это самый простой способ отправить себе на почту сообщение. Мне самому не нравится использовать для работы базового функционала сторонние библиотеки, но почтовая система до неприличия неудобная, поэтому выхода у нас нет


Вот это вы можете получить:

Ссылка для скачивания этой формы

О чем эта инструкция

Это инструкция по добавлению на ваш сайт PHPMailer и отправки писем с прикреплёнными файлами к вам на почту без перезагрузки страницы.


1. Скачиваем PHPMailer

Переходим на сайт https://github.com/PHPMailer/PHPMailer и скачиваем последнюю версию

Во всём архиве нам нужны только 3 файла:

PHPMailer-master\src\PHPMailer.php PHPMailer-master\src\SMTP.php PHPMailer-master\src\Exception.php

Всё остальное, что хранится на Github — на*уй не нужно, можете удалять.


2. Удаляем из файлов весь хлам

Этот пункт вы можете пропустить. Он не является обязательным

Файлы, написанные разработчиками PHPMailer, содержат немереное количество мусора, состоящего из одних комментариев (зачем?!). Вес этих трёх файлов вместе с комментариями равен 222кб, после удаления комментов вес составит 82кб. Почти в 3 раза…

Я предлагаю удалить все комментарии в файлах с помощью какого-нибудь “PHP минификатора”. Можете использовать любой, я взял (первый попавшийся) http://php-minify.com

  1. Открываете файл
  2. Копируете содержимое файла
  3. Вставляете в форму на сайте php-minify и жмёте “COMPRESS”
  4. Результат перезаписываем

Процедуру повторяете со всеми тремя файлами.

Для наглядности файл Exception.php

слева - до сжатия, справа - после

3. Перемещаем файлы в проект

И теперь помещаем эти 3 файла на наш сайт. Я создал специально для этих файлов папку под названием phpmailer


4. Создаём файл конфигурации

Создадим файл send.php с таким содержанием

<?php
// Файлы phpmailer
require 'phpmailer/PHPMailer.php';
require 'phpmailer/SMTP.php';
require 'phpmailer/Exception.php';

# проверка, что ошибки нет
if (!error_get_last()) {

    // Переменные, которые отправляет пользователь
    $name = $_POST['name'] ;
    $email = $_POST['email'];
    $text = $_POST['text'];
    $file = $_FILES['myfile'];
    
    
    // Формирование самого письма
    $title = "Заголовок письма";
    $body = "
    <h2>Новое письмо</h2>
    <b>Имя:</b> $name<br>
    <b>Почта:</b> $email<br><br>
    <b>Сообщение:</b><br>$text
    ";
    
    // Настройки PHPMailer
    $mail = new PHPMailer\PHPMailer\PHPMailer();
    
    $mail->isSMTP();   
    $mail->CharSet = "UTF-8";
    $mail->SMTPAuth   = true;
    //$mail->SMTPDebug = 2;
    $mail->Debugoutput = function($str, $level) {$GLOBALS['data']['debug'][] = $str;};
    
    // Настройки вашей почты
    $mail->Host       = 'smtp.yandex.ru'; // SMTP сервера вашей почты
    $mail->Username   = 'username'; // Логин на почте
    $mail->Password   = 'password'; // Пароль на почте
    $mail->SMTPSecure = 'ssl';
    $mail->Port       = 465;
    $mail->setFrom('[email protected]', 'Name'); // Адрес самой почты и имя отправителя
    
    // Получатель письма
    $mail->addAddress('[email protected]');  
    $mail->addAddress('[email protected]'); // Ещё один, если нужен
    
    // Прикрипление файлов к письму
    if (!empty($file['name'][0])) {
        for ($i = 0; $i < count($file['tmp_name']); $i++) {
            if ($file['error'][$i] === 0) 
                $mail->addAttachment($file['tmp_name'][$i], $file['name'][$i]);
        }
    }
    // Отправка сообщения
    $mail->isHTML(true);
    $mail->Subject = $title;
    $mail->Body = $body;    
    
    // Проверяем отправленность сообщения
    if ($mail->send()) {
        $data['result'] = "success";
        $data['info'] = "Сообщение успешно отправлено!";
    } else {
        $data['result'] = "error";
        $data['info'] = "Сообщение не было отправлено. Ошибка при отправке письма";
        $data['desc'] = "Причина ошибки: {$mail->ErrorInfo}";
    }
    
} else {
    $data['result'] = "error";
    $data['info'] = "В коде присутствует ошибка";
    $data['desc'] = error_get_last();
}

// Отправка результата
header('Content-Type: application/json');
echo json_encode($data);

?>

Здесь вам нужно отредактировать эти поля под себя:

// Настройки вашей почты
$mail->Host       = 'smtp.yandex.ru'; // SMTP сервера вашей почты
$mail->Username   = 'username'; // Логин на почте
$mail->Password   = 'password'; // Пароль на почте
$mail->SMTPSecure = 'ssl';
$mail->Port       = 465;
$mail->setFrom('[email protected]', 'Name'); // Адрес самой почты и имя отправителя

// Получатель письма
$mail->addAddress('[email protected]');  
$mail->addAddress('[email protected]'); // Ещё один, если нужен

Нужно использовать не пароль от самой почты, а «Пароль приложения». В настройках вашей почты, будь то Gmail, Yandex или Mailru — есть раздел «Пароли приложений», где вы можете специально создать отдельный сгенерированный пароль. Именно этот пароль нужно использовать

Сохраняем этот файл, как send.php и помещаем его в корень сайта

В примере указаны настройки для Яндекса. Вы можете использовать любую почту для отправки. Для изменения почты отправки, к примеру, на Gmail — нужно изменить эти поля:

$mail->Host = ‘smtp.gmail.com’; // SMTP сервер
$mail->SMTPSecure = ‘ssl’; // шифрование
$mail->Port = 465; // Порт

5. Создаём HTML форму

Теперь поместим эту форму в нужное место на вашем сайте

<form enctype="multipart/form-data" method="post" id="form" onsubmit="submitForm(event)" action="send.php">
	<p>Имя</p>
	<input placeholder="Представьтесь" name="name" type="text" >
	<p>Email</p>
	<input placeholder="Укажите почту" name="email" type="text" >
	<p>Сообщение</p>
	<textarea name="text"></textarea>
	<p>Прикрепить файлы</p>
	<input type="file" name="myfile[]" multiple id="myfile">
	<p><input value="Отправить" type="submit"></p>
</form>

6. Прописываем JavaScript

Нужно теперь просто где-то в коде разместить эту функцию. Вы можете её скопировать в свой script файл, который уже подключён в <head>, либо вставить его внизу перед тегом </body>.

<script>
async function submitForm(event) {
  event.preventDefault(); // отключаем перезагрузку/перенаправление страницы
  try {
  	// Формируем запрос
    const response = await fetch(event.target.action, {
    	method: 'POST',
    	body: new FormData(event.target)
    });
    // проверяем, что ответ есть
    if (!response.ok) throw (`Ошибка при обращении к серверу: ${response.status}`);
    // проверяем, что ответ действительно JSON
    const contentType = response.headers.get('content-type');
    if (!contentType || !contentType.includes('application/json')) {
      throw ('Ошибка обработки. Ответ не JSON');
    }
    // обрабатываем запрос
    const json = await response.json();
    if (json.result === "success") {
    	// в случае успеха
    	alert(json.info);
    } else { 
    	// в случае ошибки
    	console.log(json);
    	throw (json.info);
    }
  } catch (error) { // обработка ошибки
    alert(error);
  }
}
</script>

7. Всё (почти)

Форма у вас должна выглядеть вот так:

И в случае успеха (или неудачи) вы должны получить всплывающее сообщение от браузера, мол “Сообщение успешно отправлено!” или “Ошибка…”.


8. Почему ошибка?

Это очень частый вопрос, где я с полной уверенностью могу сказать: 60% причин ошибок — это ваш сервер, 30% — ваша почта, 9% — вы неверно указали данные от почты, а 1% причин —ты, с*ка, даун, который решил изменить код, а потом начинаешь искренне удивляться, почему ничерта не работает.

Во первых нужно понять, что конкретно случилось. Для этого нужно открыть консоль и включить режим debug. Он включается путём раскомментирования строки //$mail→SMTPDebug = 2; в файле send.php (просто убери //)

Консоль открывается клавишей F12 → Вкладка «Console».

Когда консоль открыта, попробуй снова нажать кнопку «Отправить» и справа у тебя должна появиться новая строка, которую можно раскрыть.

Ошибка, потому что введён неверный пароль

Вот так будет выглядеть ошибка, в случае, если что-то не так с паролем.

Error: authentication failed: Invalid user or password!

Ошибка, потому что почта блокирует работу SMTP

Да, такая дичь есть. Проверенно на свежезарегистрированных аккаунтах Яндекса. Лечится это письмом в техподдержку вашего почтового сервиса. Если возьмёте старую почту, то вероятность ошибки меньше.

Ошибка, потому что хостинг блокирует почту

В этот момент может быть вообще любая ошибка (не угадаешь), поэтому, если вы уверены, что с вашей стороны проблем нет и письма отправляются с локального компьютера, то просто пишем письмо в техподдержку хостинга, куда прикрепляем полученное сообщение из консоли.

Ошибка из-за размера данных

К примеру, человек пытается загрузить файл размером 10гб. И допустим, вы не хотите, чтобы такой большой файл нагружал сервер и занимал место. Логично было бы рассуждать, что нам стоит установить проверку размера файла на моменте после "if (!empty($file['name'][0])) {", что-то типа: "Если файл больше 10мб - отобразить ошибку". Но если вы не отключали ограничение размера файлов (50мб обычно), то у вас возникнет проблема: файл тупо не дойдёт до проверки. Если в случае с Nginx вы увидите человеческую ошибку 413, которую можно обработать, то в случае с apache всё намного сложнее. Там тупо нет нормального обработчика ошибок. И самый сок: ошибка, которую выдаст Apache будет на нулевой строке. Это значит, что обработать ошибку в коде, в случае если ошибки отображаются - невозможно. И да, ответ от сервера будет = 200, success))) Тип всё хорошо, дружище, держись там!

Допустим мы вырубаем отображение ошибок на 0 строке (параметр display_startup_errors) и проверяем, что там лежит в объекте error_get_last(). А нас там встречает вот такая дичь: "Warning: POST Content-Length of 65129587 bytes exceeds the limit of 52428800 bytes in Unknown on line 0". А теперь сиди друг и разбирай строку, чтобы нормально сообщить пользователю о передозе размера файла.

Предположим, что вы арендуете облачный сервер с apache и менять настройки его мы не можете. И предположим, что отображение ошибок на 0 строке у вас активировано. Учитывая, что этот дурак шлёт строку "Warning...", то скрипт разобрать JSON не сможет, потому что его нет. И как вы понимаете, клиент увидит ошибку "Ответ не JSON".

Как вы уже поняли, обработчика ошибки связанной с размером данных я не вставлял, так как в зависимости от сервера будет разная ошибка, поэтому вам нужно будет самому подстраиваться под ситуацию. На данный момент, если ошибки были, но они не отобразились (выключен display_startup_errors), то письмо тупо не будет отправляться. Конкретную причину мы, разумеется, не узнаем, но зато идиотского ответа, что это не JSON тоже не получим. Поэтому отключайте display_startup_errors (просите техподдержку это сделать) и пишите обработчик.


После всего, настоятельно рекомендую снова закомментировать строчку c $mail->SMTPDebug = 2


9. Скачать готовую форму

-> Скачать с Github


Внимание!

Спасибо за внимание!