Отправка писем в PHP: обзор методов и решений

Раздел: PHP -> Работа с почтой PHP

Различные способы отправки почты в PHP

Основное эффективное решение: библиотека PHPMailer с SMTP-аутентификацией

Современные почтовые серверы требуют аутентификации и шифрования. Встроенная функция mail() часто не справляется с этими задачами, особенно на общих хостингах. Библиотека PHPMailer предоставляет гибкий и надёжный способ отправки писем через SMTP с поддержкой TLS, вложений и HTML-формата.


    <?php
    require 'vendor/autoload.php';

    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\SMTP;
    use PHPMailer\PHPMailer\Exception;

    $mail = new PHPMailer(true);

    try {
        $mail->SMTPDebug = SMTP::DEBUG_SERVER;
        $mail->isSMTP();
        $mail->Host       = 'smtp.example.com';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'user@example.com';
        $mail->Password   = 'secret';
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port       = 587;

        $mail->setFrom('from@example.com', 'Отправитель');
        $mail->addAddress('to@example.com', 'Получатель');
        $mail->isHTML(true);
        $mail->Subject = 'Тема письма';
        $mail->Body    = '<b>Текст письма</b>';
        $mail->AltBody = 'Текст для почтовых клиентов без HTML';

        $mail->send();
        echo 'Письмо отправлено успешно';
    } catch (Exception $e) {
        echo "Ошибка: {$mail->ErrorInfo}";
    }
  

Для установки PHPMailer используется Composer: composer require phpmailer/phpmailer. Этот метод подходит для проектов, где требуется надёжная доставка писем с аутентификацией, SSL/TLS и поддержка современных почтовых сервисов (Gmail, Yandex, Mail.ru).

Какие проблемы возникают при использовании SMTP через PHPMailer?

  • Ошибка аутентификации: неверные логин/пароль или включённая двухфакторная аутентификация. Для Gmail требуется пароль приложения.
  • Блокировка порта: на некоторых хостингах порт 587 закрыт. Используйте порт 465 с шифрованием SSL.
  • Игнорирование SMTPDebug: отладка может выдать подробную информацию о сбое.

Как отправить письмо с помощью встроенной функции mail()?

Функция mail() доступна без дополнительных библиотек, но её работа зависит от настроек сервера (sendmail или SMTP в php.ini). Пример простого письма:


    <?php
    $to = 'to@example.com';
    $subject = 'Тема';
    $message = 'Текст письма';
    $headers = 'From: from@example.com' . "\r\n" .
               'Reply-To: reply@example.com' . "\r\n" .
               'X-Mailer: PHP/' . phpversion();

    if (mail($to, $subject, $message, $headers)) {
        echo 'Письмо отправлено';
    } else {
        echo 'Ошибка отправки';
    }
  

Этот вариант подходит для простых текстовых писем на локальных серверах или при использовании настроенного MTA. Недостатки: отсутствие аутентификации, сложности с HTML и вложениями, частые блокировки спам-фильтрами.

Типичные ошибки mail():

  • Письмо не доходит – часто из-за отсутствия заголовка From с доменом сервера.
  • Заголовки не экранированы – уязвимость для инъекций заголовков.

Какие возможности предоставляет библиотека SwiftMailer (SwiftMailer)?

SwiftMailer – альтернатива PHPMailer с похожим функционалом. Установка через Composer: composer require swiftmailer/swiftmailer. Пример отправки через SMTP:


    <?php
    require 'vendor/autoload.php';

    $transport = (new Swift_SmtpTransport('smtp.example.com', 587, 'tls'))
        ->setUsername('user@example.com')
        ->setPassword('password');

    $mailer = new Swift_Mailer($transport);

    $message = (new Swift_Message('Тема письма'))
        ->setFrom(['from@example.com' => 'Отправитель'])
        ->setTo(['to@example.com'])
        ->setBody('<b>Текст письма</b>', 'text/html')
        ->addPart('Текст для клиентов без HTML', 'text/plain');

    if ($mailer->send($message)) {
        echo 'Письмо отправлено';
    } else {
        echo 'Ошибка';
    }
  

SwiftMailer поддерживает вложения, антифлуд (ограничение количества писем в минуту) и работает с любыми почтовыми серверами. Однако с 2017 года проект перешёл в режим поддержки, рекомендуется PHPMailer для новых проектов.

Как отправить HTML-письмо с вложениями через PHPMailer?

PHPMailer позволяет легко добавлять файлы и задавать HTML-формат. Пример с вложением:


    <?php
    $mail = new PHPMailer(true);
    $mail->isSMTP();
    // ... настройки SMTP ...
    $mail->isHTML(true);
    $mail->Subject = 'Письмо с вложением';
    $mail->Body    = '<h1>Заголовок</h1><p>Текст</p>';
    $mail->AltBody = 'Альтернативный текст';
    $mail->addAttachment('/path/to/file.pdf', 'Название.pdf');
    $mail->send();
  

Для добавления нескольких файлов вызывается addAttachment() несколько раз. Метод addStringAttachment() позволяет вложить данные из строки (например, сгенерированный PDF).

Почему вложение не открывается?

  • Неверный MIME-тип – PHPMailer определяет его автоматически, но можно задать вручную третьим параметром.
  • Файл не существует – проверяйте путь.

Как настроить отправку через почтовый сервер Gmail?

Для Gmail используется SMTP-сервер smtp.gmail.com, порт 465 (SSL) или 587 (TLS). Потребуется пароль приложения (если включена двухфакторная аутентификация) или разрешён доступ для ненадёжных приложений. Пример с PHPMailer:


    $mail->Host = 'smtp.gmail.com';
    $mail->SMTPAuth = true;
    $mail->Username = 'yourname@gmail.com';
    $mail->Password = 'пароль_приложения';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // порт 465
    $mail->Port = 465;
  

Для Yandex: smtp.yandex.ru, порт 465 (SSL) или 587 (TLS). Для Mail.ru: smtp.mail.ru, порт 465 (SSL).

Почему Gmail блокирует отправку?

  • Небезопасное приложение – включите разрешение в настройках аккаунта или используйте пароль приложения.
  • Превышение лимита – Google ограничивает количество писем (500 в день для обычного аккаунта).

Как обрабатывать ошибки при отправке почты?

PHPMailer генерирует исключения при включённом режиме true в конструкторе. В блоке catch можно получить текст ошибки через $mail->ErrorInfo. Для mail() возвращается false, но детали отсутствуют. Лучше проверять логи сервера или использовать библиотеки с отладкой.


    try {
        $mail->send();
    } catch (Exception $e) {
        // логирование ошибки
        error_log($mail->ErrorInfo);
        // уведомление разработчика
    }
  

Дополнительно можно включить $mail->SMTPDebug = SMTP::DEBUG_CONNECTION; для вывода диалога с сервером.

Общие проблемы отправки писем и их решения

  • Письма попадают в спам: используйте DKIM, SPF, DMARC записи для домена; избегайте ссылок на подозрительные ресурсы; добавляйте текстовую альтернативу HTML-письму.
  • Кодировка символов: задавайте кодировку письма явно, например, $mail->CharSet = 'UTF-8';.
  • Функция mail() не работает на хостинге: обратитесь к провайдеру для настройки sendmail или используйте SMTP-библиотеку.

Расширенные примеры с комментариями и результатами.

Пример 1: Полный класс для отправки через PHPMailer с логированием

Пример

    <?php
    require 'vendor/autoload.php';

    class MailSender {
        private $mail;

        public function __construct() {
            $this->mail = new PHPMailer(true);
            $this->setup();
        }

        private function setup() {
            $this->mail->isSMTP();
            $this->mail->Host       = 'smtp.example.com';
            $this->mail->SMTPAuth   = true;
            $this->mail->Username   = 'user@example.com';
            $this->mail->Password   = 'your_password';
            $this->mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
            $this->mail->Port       = 587;
            $this->mail->CharSet    = 'UTF-8';
        }

        public function send($to, $subject, $body, $altBody = '', $attachments = []) {
            try {
                $this->mail->setFrom('from@example.com', 'Sender');
                $this->mail->addAddress($to);
                $this->mail->isHTML(true);
                $this->mail->Subject = $subject;
                $this->mail->Body    = $body;
                $this->mail->AltBody = $altBody ?: strip_tags($body);

                foreach ($attachments as $file) {
                    $this->mail->addAttachment($file['path'], $file['name'] ?? '');
                }

                if ($this->mail->send()) {
                    return ['status' => true, 'message' => 'Письмо отправлено'];
                }
            } catch (Exception $e) {
                return ['status' => false, 'message' => $this->mail->ErrorInfo];
            }
        }
    }

    $sender = new MailSender();
    $result = $sender->send(
        'client@example.com',
        'Заказ №123',
        '<h1>Спасибо за заказ</h1><p>Ваш заказ обрабатывается.</p>',
        'Ваш заказ обрабатывается.',
        [['path' => '/tmp/invoice.pdf', 'name' => 'invoice.pdf']]
    );
    echo json_encode($result);
    {"status":true,"message":"Письмо отправлено"}

Результат успешной отправки. В случае ошибки в поле message будет текст ошибки.

Пример 2: Отправка письма с встроенным изображением (embedded image)

Пример

    <?php
    $mail = new PHPMailer(true);
    // ... настройки SMTP ...
    $mail->isHTML(true);
    $mail->Subject = 'Письмо с изображением';
    $mail->Body    = '<h1>Картинка внутри</h1><img src="cid:myimage">';
    $mail->addEmbeddedImage('/path/to/photo.jpg', 'myimage', 'photo.jpg');
    $mail->send();

Изображение вставляется как CID (Content-ID), в браузере получателя оно будет показано прямо в письме, без внешних ссылок.

Пример 3: Отправка писем через очередь с использованием SwiftMailer и ограничением по времени

Пример

    <?php
    require 'vendor/autoload.php';

    $transport = (new Swift_SmtpTransport('smtp.example.com', 587, 'tls'))
        ->setUsername('user')
        ->setPassword('pass');

    $mailer = new Swift_Mailer($transport);
    // Ограничение: не более 10 писем в минуту
    $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin(10, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE));

    $messages = [
        ['to' => 'a@example.com', 'body' => 'Письмо 1'],
        ['to' => 'b@example.com', 'body' => 'Письмо 2']
    ];

    foreach ($messages as $msg) {
        $message = (new Swift_Message('Уведомление'))
            ->setFrom(['from@example.com'])
            ->setTo([$msg['to']])
            ->setBody($msg['body']);
        $mailer->send($message);
    }

Плагин ThrottlerPlugin предотвращает превышение лимитов почтового сервера.

Пример 4: Отправка письма с помощью mail() с кириллицей и заголовками

Пример

    <?php
    $to = 'recipient@example.com';
    $subject = '=?UTF-8?B?' . base64_encode('Тема на русском') . '?=';
    $message = 'Простой текст на русском языке';
    $headers = "MIME-Version: 1.0\r\n";
    $headers .= "Content-type: text/plain; charset=UTF-8\r\n";
    $headers .= "From: sender@example.com\r\n";

    if (mail($to, $subject, $message, $headers)) {
        echo 'Письмо отправлено';
    } else {
        echo 'Ошибка отправки';
    }

Кодировка UTF-8 задаётся явно, а тема кодируется в Base64 для корректного отображения.

Отправка почты через PHP - comments

En
Php отправленные (php)