Практическое руководство: почтовые рассылки через PHP
Отправка почты через PHP
Для отправки электронных писем из PHP существует несколько основных подходов. Наиболее надёжным и гибким является использование специализированных библиотек, таких как PHPMailer, с подключением к внешнему SMTP-серверу. Однако в некоторых простых сценариях можно обойтись встроенной функцией mail(). Рассмотрим различные варианты.
PHPMailer + SMTP (рекомендуемый способ)
Как отправить письмо через внешний SMTP-сервер, например Gmail или Яндекс?
PHPMailer - это популярная библиотека, которая предоставляет удобный интерфейс для отправки почты через SMTP, с поддержкой вложений, HTML, альтернативных кодировок и аутентификации. Установка через Composer: composer require phpmailer/phpmailer.
require 'vendor/autoload.php';
$mail = new PHPMailer\PHPMailer\PHPMailer();
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->Port = 587;
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->SMTPAuth = true;
$mail->Username = 'your@gmail.com';
$mail->Password = 'your-app-password';
$mail->setFrom('your@gmail.com', 'Sender');
$mail->addAddress('recipient@example.com', 'Recipient');
$mail->Subject = 'Test from PHPMailer';
$mail->Body = 'This is a <b>test</b> email.';
$mail->isHTML(true);
if ($mail->send()) {
echo 'Mail sent successfully';
} else {
echo 'Error: ' . $mail->ErrorInfo;
}Php скрипт почты (отправка почты через php)
Типичные проблемы:
- Ошибка аутентификации - неверный логин/пароль или требуется пароль приложения (для Gmail нужно включить двухфакторную аутентификацию и создать пароль приложения).
- Блокировка SMTP - некоторые хостинги блокируют исходящие соединения на порты SMTP. Необходимо использовать порт 587 (STARTTLS) или 465 (SSL), или настроить ретрансляцию через локальный почтовый сервер.
- Таймауты - если сервер не отвечает, увеличить таймаут:
$mail->Timeout = 60;.
Вариант 1: Функция mail()
Как отправить письмо без установки дополнительных библиотек?
Функция mail() является встроенной в PHP и позволяет отправить письмо через локальный MTA (sendmail, Postfix и т.д.). Простейший пример:
$to = 'recipient@example.com';
$subject = 'Test mail';
$message = 'Hello!';
$headers = 'From: sender@example.com' . "\r\n" .
'Reply-To: sender@example.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
if (mail($to, $subject, $message, $headers)) {
echo 'Mail sent';
} else {
echo 'Failed';
}Ограничения и проблемы:
- Письма часто попадают в спам, так как отсутствуют SPF/DKIM записи.
- Сложно отправить HTML-письмо с корректными заголовками Content-Type.
- Не поддерживает SMTP-аутентификацию - письмо отправляется от имени сервера, а не от указанного From, если сервер не настроен.
- На некоторых хостингах функция mail() отключена.
Решение: использовать библиотеки или настроить sendmail с внешним SMTP-ретранслятором.
Вариант 2: swiftmailer (устаревший)
Как работать с библиотекой SwiftMailer для отправки писем?
SwiftMailer ранее была популярна, но сейчас её разработка прекращена в пользу Symfony Mailer. Пример:
require 'vendor/autoload.php';
$transport = (new Swift_SmtpTransport('smtp.gmail.com', 587, 'tls'))
->setUsername('your@gmail.com')
->setPassword('your-app-password');
$mailer = new Swift_Mailer($transport);
$message = (new Swift_Message('Subject'))
->setFrom(['your@gmail.com' => 'Sender'])
->setTo(['recipient@example.com'])
->setBody('Hello', 'text/plain');
$result = $mailer->send($message);Проблема: библиотека больше не обновляется, рекомендуется перейти на Symfony Mailer.
Вариант 3: Symfony Mailer
Как отправлять письма в современных PHP-проектах?
Symfony Mailer - современная библиотека от Symfony, которая также может использоваться вне фреймворка. Установка: composer require symfony/mailer. Пример:
require 'vendor/autoload.php';
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mime\Email;
$transport = Transport::fromDsn('smtp://your@gmail.com:your-app-password@smtp.gmail.com:587');
$mailer = new Mailer($transport);
$email = (new Email())
->from('your@gmail.com')
->to('recipient@example.com')
->subject('Symfony Mailer test')
->text('Hello!');
$mailer->send($email);Возможные проблемы: неправильный DSN формат, нехватка прав доступа. Рекомендуется использовать переменные окружения для паролей.
Вариант 4: Отправка через API SendGrid или Mailgun
Как отправлять письма через внешние почтовые сервисы, избегая проблем с SMTP?
Сервисы SendGrid, Mailgun, Amazon SES предоставляют HTTP API. Пример через cURL (SendGrid):
$url = 'https://api.sendgrid.com/v3/mail/send';
$apiKey = 'YOUR_API_KEY';
$data = [
'personalizations' => [
['to' => [['email' => 'recipient@example.com']]]
],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Test via SendGrid',
'content' => [['type' => 'text/plain', 'value' => 'Hello']]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 202) { echo 'Sent'; } else { echo 'Error: ' . $response; }Проблемы: необходимо иметь аккаунт на сервисе, ограничения бесплатных тарифов, возможные задержки.
Дополнительные примеры и расширенные сценарии
Пример 1: Отправка письма с вложением и inline-изображением (PHPMailer)
Этот пример демонстрирует добавление файла для скачивания и встраивание изображения прямо в тело письма.
require 'vendor/autoload.php';
$mail = new PHPMailer\PHPMailer\PHPMailer();
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->Port = 587;
$mail->SMTPSecure = 'tls';
$mail->SMTPAuth = true;
$mail->Username = 'your@gmail.com';
$mail->Password = 'your-app-password';
$mail->setFrom('your@gmail.com', 'Sender');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'With attachment and inline image';
// Добавляем вложение
$mail->addAttachment('/path/to/document.pdf', 'document.pdf');
// Встраиваем изображение
$mail->addEmbeddedImage('/path/to/logo.png', 'logo_cid');
// HTML тело письма ссылается на изображение по cid
$mail->Body = 'Вложение
Посмотрите документ в приложении.
';
$mail->isHTML(true);
if ($mail->send()) {
echo 'Письмо отправлено успешно';
} else {
echo 'Ошибка: ' . $mail->ErrorInfo;
}Письмо отправлено успешно
Пример 2: Массовая рассылка с задержкой и логированием ошибок
При отправке большого количества писем важно соблюдать лимиты SMTP-сервера и фиксировать неудачные попытки.
$recipients = ['a@example.com', 'b@example.com', 'c@example.com'];
$logFile = 'send_log.txt';
foreach ($recipients as $to) {
try {
$mail = new PHPMailer\PHPMailer\PHPMailer(true); // true включает исключения
// Настройки SMTP (вынесены в функцию для краткости)
// ... $mail->isSMTP(), Host, Port, Auth, Username, Password ...
$mail->setFrom('sender@example.com');
$mail->addAddress($to);
$mail->Subject = 'Bulk test';
$mail->Body = 'Hello ' . $to;
$mail->send();
$log = date('Y-m-d H:i:s') . " - Success: $to\n";
file_put_contents($logFile, $log, FILE_APPEND);
sleep(2); // пауза между письмами
} catch (Exception $e) {
$log = date('Y-m-d H:i:s') . " - Failed: $to - " . $mail->ErrorInfo . "\n";
file_put_contents($logFile, $log, FILE_APPEND);
echo "Ошибка при отправке $to: " . $mail->ErrorInfo . "\n";
}
}(в файл send_log.txt записываются строки) 2025-04-03 12:00:00 - Success: a@example.com 2025-04-03 12:00:02 - Success: b@example.com 2025-04-03 12:00:04 - Failed: c@example.com - SMTP Error: ...
Пример 3: Использование DKIM подписи для улучшения доставляемости (PHPMailer)
DKIM добавляет цифровую подпись к письму, подтверждая, что оно отправлено с вашего домена. Требуется закрытый ключ и открытый DNS-запись.
$mail = new PHPMailer\PHPMailer\PHPMailer();
// базовые настройки SMTP...
$mail->DKIM_domain = 'example.com';
$mail->DKIM_private = '/path/to/private.key';
$mail->DKIM_selector = 'default'; // должно совпадать с DNS записью
$mail->DKIM_passphrase = ''; // если ключ защищен паролем
// после настройки $mail->send() автоматически добавит заголовок DKIM-Signature(заголовки отправленного письма будут содержать DKIM-Signature: ...)
Пример 4: Отправка письма с multipart/alternative (текст+HTML) без библиотек
Этот пример показывает, как вручную сформировать письмо с двумя частями через функцию mail().
$to = 'recipient@example.com';
$subject = 'Multipart example';
$boundary = md5(time());
$headers = "MIME-Version: 1.0\r\n";
$headers .= "From: sender@example.com\r\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"$boundary\"\r\n";
$body = "--$boundary\r\n";
$body .= "Content-Type: text/plain; charset=UTF-8\r\n\r\n";
$body .= "Текстовая версия письма.\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Type: text/html; charset=UTF-8\r\n\r\n";
$body .= "HTML версия
Текст
\r\n";
$body .= "--$boundary--\r\n";
mail($to, $subject, $body, $headers);Письмо отправлено (результат зависит от локального MTA)