Отправка email через HTTPS в PHP: от cURL до Guzzle
Введение в отправку email через HTTPS в PHP
Традиционная функция mail() использует SMTP и не всегда подходит для современных требований безопасности и доставляемости. Альтернативный подход – отправка писем через HTTPS-запросы к REST API специализированных почтовых сервисов (SendGrid, Mailgun, Amazon SES). Это обеспечивает шифрование, аутентификацию и высокую надёжность. В статье рассмотрены основные варианты реализации на PHP.
Наиболее эффективное решение: Guzzle и SendGrid
Библиотека Guzzle предоставляет удобный интерфейс для HTTP-запросов. Отправка через SendGrid требует установки пакета и API-ключа.
// Установка через Composer (выполнить в терминале):
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://api.sendgrid.com/v3/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('SENDGRID_API_KEY'),
'Content-Type' => 'application/json',
]
]);
$emailData = [
'personalizations' => [[
'to' => [['email' => 'recipient@example.com']]
]],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Тестовое письмо',
'content' => [['type' => 'text/plain', 'value' => 'Привет! Это тест.']]
];
try {
$response = $client->post('mail/send', [
'json' => $emailData
]);
if ($response->getStatusCode() === 202) {
echo "Письмо успешно отправлено";
}
} catch (\GuzzleHttp\Exception\ClientException $e) {
$status = $e->getResponse()->getStatusCode();
$body = (string) $e->getResponse()->getBody();
echo "Ошибка $status: $body";
}
Проблемы и их решение
- Отсутствует Composer или не установлен Guzzle – проверьте наличие composer.json и выполните установку.
- Ошибка аутентификации (401) – убедитесь, что API-ключ корректен и экспортирован в переменную окружения SENDGRID_API_KEY.
- SSL-ошибка – в большинстве случаев Guzzle обрабатывает сертификаты автоматически, но при работе в изолированной среде может потребоваться указать путь к CA bundle через параметр verify.
Как отправить email через HTTPS без внешних библиотек? Вариант с cURL
Если установка Composer невозможна, используется нативная библиотека cURL. Она присутствует в большинстве PHP-сборок.
$apiKey = getenv('SENDGRID_API_KEY');
$url = 'https://api.sendgrid.com/v3/mail/send';
$data = [
'personalizations' => [[
'to' => [['email' => 'recipient@example.com']]
]],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Тест cURL',
'content' => [['type' => 'text/plain', 'value' => 'Отправлено через cURL.']]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
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_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode === 202) {
echo "Письмо отправлено (HTTP $httpCode)";
} else {
echo "Ошибка: HTTP $httpCode, ответ: " . $response;
}
curl_close($ch);
Типичные ошибки cURL
- CURL error 60 – SSL certificate problem – отключите проверку (только для теста!) установкой CURLOPT_SSL_VERIFYPEER = false или укажите правильный CA bundle.
- Ошибка таймаута – добавьте curl_setopt($ch, CURLOPT_TIMEOUT, 30);.
- Некорректный JSON – проверьте структуру данных перед кодированием.
Как отправить email через HTTPS используя только встроенные функции PHP? Вариант с file_get_contents и stream context
Для простых запросов можно обойтись без cURL, применив функцию file_get_contents() с контекстом потока. Этот метод работает только при включённой директиве allow_url_fopen.
$apiKey = getenv('SENDGRID_API_KEY');
$url = 'https://api.sendgrid.com/v3/mail/send';
$data = [
'personalizations' => [[
'to' => [['email' => 'recipient@example.com']]
]],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Тест file_get_contents',
'content' => [['type' => 'text/plain', 'value' => 'Отправлено без cURL.']]
];
$options = [
'http' => [
'method' => 'POST',
'header' => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
],
'content' => json_encode($data),
'ignore_errors' => true
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true
]
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
$httpResponseHeader = $http_response_header;
// Поиск HTTP-статуса в заголовках
if (strpos($httpResponseHeader[0], '202') !== false) {
echo "Письмо отправлено";
} else {
echo "Ошибка: " . implode('\n', $httpResponseHeader);
}
Проблемы и ограничения
- allow_url_fopen отключена – метод не сработает. Используйте cURL или Guzzle.
- Трудности с обработкой ошибок – $http_response_header не всегда даёт полную информацию. Для надёжности применяйте проверку кода ответа через парсинг первой строки.
- SSL-сертификаты – при ошибках верификации укажите 'verify_peer' => false (только для отладки).
Как отправить email через HTTPS с помощью PHPMailer и REST API?
PHPMailer – популярная библиотека для работы с почтой, но она ориентирована на SMTP. Для отправки через HTTPS можно использовать её в паре с обёрткой над REST API, например, phpmailer/phpmailer совместно с кастомным транспортом. Однако предпочтительнее отдельные HTTP-клиенты. В качестве альтернативы – подключить API напрямую через Guzzle внутри обработчика PHPMailer.
// Пример отправки через SendGrid с использованием PHPMailer (транспорт не задан)
// Фактически такой код дублирует функциональность Guzzle. Лучше использовать готовый SDK.
Полноценная интеграция PHPMailer с HTTPS-сервисами выходит за рамки статьи, но упомянуть стоит для полноты картины.
Расширенные примеры отправки email через HTTPS
Отправка с вложением (Guzzle + SendGrid)
$emailData = [
'personalizations' => [[
'to' => [['email' => 'recipient@example.com']]
]],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Письмо с файлом',
'content' => [['type' => 'text/plain', 'value' => 'Смотри вложение.']],
'attachments' => [[
'content' => base64_encode(file_get_contents('document.pdf')),
'filename' => 'document.pdf',
'type' => 'application/pdf',
'disposition' => 'attachment'
]]
];
$response = $client->post('mail/send', ['json' => $emailData]);
echo $response->getStatusCode() === 202 ? 'Успех' : 'Ошибка';
// При успехе: HTTP 202
Отправка нескольким получателям (cURL)
$data['personalizations'][0]['to'] = [
['email' => 'user1@example.com'],
['email' => 'user2@example.com']
];
// Остальной код без изменений
// Успех – два письма уйдут одновременно
Асинхронная отправка через Guzzle с Promise
use GuzzleHttp\Promise;
$promises = [];
foreach ($recipients as $email) {
$promises[] = $client->postAsync('mail/send', [
'json' => [
'personalizations' => [['to' => [['email' => $email]]]],
'from' => ['email' => 'sender@example.com'],
'subject' => 'Асинхронная отправка',
'content' => [['type' => 'text/plain', 'value' => 'Тест асинхронности']]
]
]);
}
$results = Promise\unwrap($promises);
foreach ($results as $result) {
echo $result->getStatusCode() . "\n";
}
202 202 ...
Отправка через Mailgun (альтернативный сервис)
$domain = 'sandbox.mailgun.org';
$apiKey = getenv('MAILGUN_API_KEY');
$client = new Client(['base_uri' => "https://api.mailgun.net/v3/$domain/"]);
$response = $client->post('messages', [
'auth' => ['api', $apiKey],
'form_params' => [
'from' => 'sender@example.com',
'to' => 'recipient@example.com',
'subject' => 'Привет от Mailgun',
'text' => 'Тестовое сообщение'
]
]);
echo $response->getStatusCode();
200
Обработка специфических HTTP-ошибок (Guzzle)
try {
$response = $client->post('mail/send', ['json' => $emailData]);
} catch (\GuzzleHttp\Exception\ClientException $e) {
$response = $e->getResponse();
$statusCode = $response->getStatusCode();
$errorBody = json_decode((string) $response->getBody(), true);
switch ($statusCode) {
case 400:
echo "Некорректный запрос: " . ($errorBody['errors'][0]['message'] ?? 'неизвестно');
break;
case 401:
echo "Неверный API-ключ";
break;
case 429:
echo "Превышен лимит запросов";
break;
default:
echo "Ошибка $statusCode";
}
}