Отправка почты средствами PHP может быть очень простой, а может быть очень сложной, это зависит от того, что и как отправлять
Простое электронное письмо – это обычный текст, и не составляет большого труда для отправки, но когда требуется отправить HTML письмо или письмо с вложенным файлом требуется указать MIME заголовки, писать содержимое файла в письмо и еще целая куча не очень приятных действий и может стать сложной задачей для разработчика.
Но используя класс PHPMailer, отправка писем становится довольно простой задачей. Этот класс берет на себя всю рутинную работу по отправки письма и представляет очень удобный интерфейс для работы с ним, к тому же и бесплатен.
Скачать PHPMailer для PHP4:
phpmailer_v2.0.4_php4
Скачать PHPMailer для PHP5/6:
phpmailer_v5.1_php5_php6
PHPMailer позволяет:
* Посылать письма с множественными: адресатами (TO), копиями (CC), скрытыми копиями (BCC) и устанавливать атрибут REPLY-TO;<.
, и пригодного для печати формата;
* Работает на любой win32 и *nix платформе;
Методы:
void AddAddress (string $address, [string $name = »]) — Добавление адресов получателей письма
bool AddAttachment (string $path, [string $name = »], [string $encoding = ‘base64’], [string $type = ‘application/octet-stream’]) — Прикрепляет файл к письму
void AddBCC (string $address, [string $name = »]) — Добавляет адрес кому отправится скрытая копия
void AddCC (string $address, [string $name = »]) — добавляет адрес кому отправится копия письма
void AddCustomHeader (mixed $custom_header) — Добавление пользовательский заголовок к письму
bool AddEmbeddedImage (string $path, string $cid, [string $name = »], [string $encoding = ‘base64’], [string $type = ‘application/octet-stream’]) — Прикрепляет к письму картинку
void AddReplyTo (string $address, [string $name = »]) — Адрес для ответа на ваше письмо.
умолчанию, адрес для ответа совпадает с адресом, с которого вы отослали письмо (FROM)
void AddStringAttachment (string $string, string $filename, [string $encoding = ‘base64’], [string $type = ‘application/octet-stream’]) — Прикрепляет файл содержимое которого находится в строке
void ClearAddresses () — Удаляет всех занесенных получателей письма
void ClearAllRecipients () — Удаляет всех получателей в том числе и кому копии должны прийти и скрытых получателей
void ClearAttachments () — Удаляет все прикрепленные файлы из письма
void ClearBCCs () — Очищает список скрытых получателей письма
void ClearCCs () — Очищает получателей копий письма
void ClearCustomHeaders () — Удаляет установленные пользовательские заголовки
void ClearReplyTos () — Удаляет адреса для ответов на письмо
void getFile (string $filename)void HeaderLine (mixed $name, mixed $value)bool IsError () — Возвращает true, если произошла ошибка
void IsHTML (bool $bool) — Устанавливает что п.
ng>bool Send () — Создает сообщение и отправляет его если сообщение не было отправлено, то возвращает false и в переменной ErrorInfo описание ошибки
bool SetLanguage (string $lang_type, [string $lang_path = ‘language/’]) — Устанавливает язык сообщений об ошибках возвращает false если не может загрузить языковой файл
void SmtpClose () — Закрывает SMTP сессию, если таковая существует
Свойства:
string $AltBody — Устанавливает тело сообщения для тех клиентов которые не поддерживают HTML
mixed $attachment — Массив Прикрепленных файлов
mixed $bcc — Массив адресов кому отправится скрытая копия письма
string $Body — Устанавливает тело сообщения может быть в HTML или текстовом виде
mixed $boundarymixed $cc — Массив адресов кому отправится копия письма
string $CharSet — Устанавливает кодировку письма
string $ConfirmReadingTo — Устанавливает адрес кому будет отправлено подтверждение о прочтении
string $ContentType — Устанавливает Content-type письма.
mixed $CustomHeader — Массив пользовательских заголовков
string $Encoding — Устанавливает как кодировку письма.
зможные варианты «8bit», «7bit», «binary», «base64», and «quoted-printable».
string $ErrorInfo — Содержит последнее сообщение об ошибке
mixed $error_countstring $From — Устанавливает адрес отправителя
string $FromName — Устанавливает имя отправителя
string $Helostring $Host — Список SMTP хостов все хосты должны быть разделены точкой с запятой, также можно указать порт например (smtp1.example.com: 25; smtp2.example.com)
string $Mailer — Метод отправки письма: («mail», «sendmail», or «smtp»)
string $MessageID — Устанавливает идентификатор сообщения, которые будут использоваться в заголовке Message-Id
string $Password — SMTP пароль
string $PluginDir — Путь к PHPMailer плагинам. Сейчас используется если SMTP класс н.
trong>mixed $sign_key_pass
bool $SingleTo
mixed $smtp
bool $SMTPAuth — Вкличает или отключает SMTP авторизацию использует Username и Password
bool $SMTPDebug — Вкличает или отключает отладку SMTP
bool $SMTPKeepAlive — Если установлено в true то необходимо явно закрыть SMTP соединение SmtpClose ()
string $SMTPSecure — Устанавливает префикс соединения «», «ssl» или «tls»
string $Subject — Устанавливает тему письма
int $Timeout — Устанавливает timeout для SMTP соединения. Не работает в win32
mixed $to — Массив адресов кому отправится письмо
string $Username — Устанавливает SMTP пользователя
string $Version — Версия PHPMailer-a
int $WordWrap — Устанавливает количество символов в строке и если необходимо переносит на новую строку
Пример:
www.webmancer.org
Вам не хватает директивы, в которой указано, что соединение использует SSL
require ("class.phpmailer.php"); $mail = new PHPMailer(); $mail->IsSMTP(); $mail->SMTPAuth = true; // turn of SMTP authentication $mail->Username = "YAHOO ACCOUNT"; // SMTP username $mail->Password = "YAHOO ACCOUNT PASSWORD"; // SMTP password $mail->SMTPSecure = "ssl"; $mail->Host = "YAHOO HOST"; // SMTP host $mail->Port = 465;
Затем добавьте другие части
$webmaster_email = "myemail@gmail.com"; //Reply to this email ID $email="myyahoomail@yahoo.in"; // Recipients email ID $name="My Name"; // Recipient name $mail->From = $webmaster_email; $mail->FromName = "My Name"; $mail->AddAddress($email,$name); $mail->AddReplyTo($webmaster_email,"My Name"); $mail->WordWrap = 50; // set word wrap $mail->IsHTML(true); // send as HTML $mail->Subject = "subject"; $mail->Body = "Hi, This is the HTML BODY "; //HTML Body $mail->AltBody = "This is the body when user views in plain text format"; //Text Body if(!$mail->Send()) { echo "Mailer Error: " . $mail->ErrorInfo; } else { echo "Message has been sent"; }
В качестве побочного примечания у меня были проблемы с использованием Body + AltBody вместе, хотя они должны работать. В результате я написал следующую функцию-оболочку, которая отлично работает.
<?php require ("class.phpmailer.php"); // Setup Configuration for Mail Server Settings $email['host'] = 'smtp.email.com'; $email['port'] = 366; $email['user'] = 'from@email.com'; $email['pass'] = 'from password'; $email['from'] = 'From Name'; $email['reply'] = 'replyto@email.com'; $email['replyname'] = 'Reply To Name'; $addresses_to_mail_to = 'email1@email.com;email2@email.com'; $email_subject = 'My Subject'; $email_body = '<html>Code Here</html>'; $who_is_receiving_name = 'John Smith'; $result = sendmail( $email_body, $email_subject, $addresses_to_mail_to, $who_is_receiving_name ); var_export($result); function sendmail($body, $subject, $to, $name, $attach = "") { global $email; $return = false; $mail = new PHPMailer(true); // the true param means it will throw exceptions on errors, which we need to catch $mail->IsSMTP(); // telling the class to use SMTP try { $mail->Host = $email['host']; // SMTP server // $mail->SMTPDebug = 2; // enables SMTP debug information (for testing) $mail->SMTPAuth = true; // enable SMTP authentication $mail->Host = $email['host']; // sets the SMTP server $mail->Port = $email['port']; // set the SMTP port for t.
->Subject = $subject; $mail->AltBody = 'To view the message, please use an HTML compatible email viewer!'; // optional - MsgHTML will create an alternate automatically $mail->MsgHTML($body); if(is_array($attach)) { foreach($attach as $attach_f) { if($attach_f != "") { $mail->AddAttachment($attach_f); // attachment } } } else { if($attach != "") { $mail->AddAttachment($attach); // attachment } } $mail->Send(); } catch (phpmailerException $e) { $return = $e->errorMessage(); } catch (Exception $e) { $return = $e->errorMessage(); } return $return; }
qaru.site
function emailsSend($recipients) { $app = new SlimSlim(); $app->db = function () { return new Capsule(); }; try { foreach ($recipients as $r.
00%" align="center" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center" valign="top" style="margin: 0; padding: 0;"> <table width="600" align="center" bgcolor="#ffffff" border="0" cellspacing="0" cellpadding="0" style="font-family:Arial, Helvetica, sans-serif;"> <tr> <td align="center" valign="top" style="margin: 0; padding: 0;"> <img style="display: block;" src="http://prp.dev.plej.pl/mailing/mailing_header_top.gif" alt="" width="600" height="240"> </td> </tr> <tr> <td style="text-align: center;"> <h1 style="font-weight: bold; font-size: 26px; color: #D1AC50; line-height: 38px;">' . $recipient['fullName'] .
039;</h1> <p style="font-size: 20px; color: #162C53; line-height: 28px;">Wysłaliśmy, specjalnie dla Ciebie zrobioną<br> Kartkę Świąteczną<br> Kliknij poniżej aby ją zobaczyć</p> </td> </tr> <tr> <td align="center" width="271" style="padding: 15px 0; height: 50px;"> <a href="/' . $recipient['token'] . '" style="width: 271px; display: block; height: 50px;"><img style="display: block;" align="center" src="http://prp.dev.plej.pl/mailing/open-pl.jpg" alt="" width="271" height="50"></a> </td> </tr> <tr> <td align="center" valign="top" style="margin: 0; padding: 0;"> <img style="display: block;" src="http://prp.dev.plej.pl/mailing/mailing_header_bottom.gif" alt="" width="600" height="210"> </td> </tr> </table> </td> </tr> </table>'; $mail = new PHPMailer(); $mail->isSMTP(); $mail->Host = 'smtp.gmail.com'; // Specify main and backup SMTP servers $mail->SMTPAuth = true; // Enable SMTP authentication $mail->Username = 'jaroslaw.frydrych@plej.pl'; // SMTP username $mail->Password = 'PaulinA2121'; // SMTP password $mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted $mail->Port = 587; // TCP port to connect to $mail->CharSet = 'UTF-8'; $mail->setFrom('jaroslaw.frydrych@plej.pl', 'Mailer'); $mail->addReplyTo('jaroslaw.frydrych@plej.pl', 'Information'); $mail->isHTML(true); $mail->Subject = 'Hej, ' . $recipient['fullName'] . ' mamy życzenia dla Ciebie'; $mail->Body = $body; //$mail->AltBody = 'This is the body in plain text for non-HTML mail clients'; $mail->addAddress($recipient['email'], $recipient['fullName']); if (!$mail->send()) { return false; } else { $app->db->table('recipients')->where('token', $recipient['token'])->update(array('sendEmail' => 1, 'sendDate' => date("Y-m-d H:i:s"))); } } } catch (Exception $e) { throw new Exception("Nie udało się wysłać wiadomości 🙁 " . $mail->ErrorInfo); } return true; }
hotexamples.com
7 преимуществ использования PHPMailer
Существует целый ряд преимуществ использования PHPMailer для отправки электронных писем.
Объектно-ориентированный подход
Функция mail() не является объектно-ориентированной, в то время как PHPMailer имеет объектно-ориентированный интерфейс.
Возможность обойтись без заголовков и избежать грязного кода
В PHPMailer вам не придется писать грязный код и создавать заголовки, как в функции mail().
Ограничения локального почтового сервера
Для функции mail() необходим локальный почтовый сервер, тогда как PHPMailer осуществляет PHP отправку почты через SMTP. Кроме этого необходимы учетные данные.
Сообщение об ошибке на нескольких языках
Библиотека PHPMailer позволяет отправлять сообщения об ошибках более чем на 40 языках.
SSL аутентификация
Библиотека PHPMailer полностью поддерживает протокол SMTP и обеспечивает аутентификацию через SSL и TLS.
Обычная текстовая версия электронных писем
PHPMailer поддерживает отправку обычного текста для почтовых клиентов без поддержки HTML.
Поддержка сообщества
Существует активное сообщество поддержки библиотеки PHPMailer, которое постоянно актуализирует ее.
Приложения
PHPMailer используется популярными CMS, такими как Joomla, Drupal и WordPress.
Установка библиотеки PHPMailer:
Библиотека может быть установлена с помощью Composer, как показано ниже:
Отправка электронной почты с помощью PHPMailer на локальном веб-сервере
Скрипт отправки формы на почту PHP:
Отправка электронной почты с вложениями:
Также с помощью PHPMailer можно осуществлять PHP отправку почты с вложением.
Файлы File.txt и images/profile.png были прикреплены. Они находятся в одной директории. Вложения могут прикрепляться с помощью вызова объекта PHPMailer addAttachemnt. Для этого нужно вызывать объект каждый раз, когда необходимо прикрепить вложения.
При использовании SMTP:
Также можно отправить электронную почту с помощью SMTP, но для этого потребуется аутентификация email с другого хоста. Например, можно создать учетную запись на Hotmail для отправки электронной почты через этот сервис. SMTP — это протокол, который отправляет запросы электронной почты к почтовому серверу, и после проверки отправляет запросы к почтовому серверу получателя.
Рассмотрим пример отправки почты с сайта PHP с использованием протокола SMTP для почтового сервера Gmail.
Перед PHP отправкой почты через SMTP необходимо задать имя хоста, номер порта и шифрование. Также может потребоваться имя пользователя и пароль для аутентификации. Следует отметить, что не получится отправить письмо на Gmail, если включены два фактора аутентификации. Для этого потребуется дополнительная настройка.
Преимущество использования удаленного SMTP:
Основное преимущество использования удаленного SMTP заключается в том, что для функции mail(), если для отправителя установлено не имя локального домена, сообщение будет помечено сервером получателя, как спам.
Предположим, что вы являетесь владельцем домена abc.com. Когда вы отправляете электронное письмо, то указываете себя, отправителя, как name@gmail.com, а получателя, как name@yahoo.com. После этого почтовый сервер Yahoo помечает ваше письмо, как спам.
Извлечение электронных писем с помощью POP3:
PHPMailer поддерживает верификацию POP перед SMTP для отправки электронных писем. Следовательно, данная библиотека позволяет отправлять файл на почту PHP с помощью SMTP, а верификацию производить через POP. Но также можно получать письма с почтовых серверов по протоколу POP3.
Сообщения об ошибках:
$mail ->ErrorInfo используется для вывода сообщений более чем на 40 языках. Для просмотра сообщений об ошибках на любом доступном языке скопируйте каталог языка из исходного кода PHPMailer в каталог проекта. Рассмотрим следующий пример, в котором для объекта PHPMailer установлен русский язык.
PHPMailer — это надежное решение для работы с электронной почтой
Мы рассмотрели альтернативный подход PHP для отправки электронной почты. Любой PHP-разработчик не может обойтись без реализации функции отправки электронных писем. Также для этого можно использовать сторонние сервисы. Кроме PHPMailer есть и другие альтернативы, такие как Zend mail и swiftmailer.
Перевод статьи «How To Send Emails In PHP Using PHPMailer Library» дружной командой проекта Сайтостроение от А до Я.
www.internet-technologies.ru
I was with the same problem except with a slight difference, the version of PHPMailer 6.0, by the good friend avs099 I know that the new version of PHPMailer since February 2018 does not support the autoload, and had a serious problem to instantiate the libraries with the namespace in MVC, I leave the code for those who need it.
//Controller protected function getLibraryWNS($libreria) { $rutaLibreria = ROOT . 'libs' . DS . $libreria . '.php'; if(is_readable($rutaLibreria)){ require_once $rutaLibreria; echo $rutaLibreria . '<br/>'; } else{ throw new Exception('Error de libreria'); } } //loginController public function enviarEmail($email, $nombre, $asunto, $cuerpo){ //Import the PHPMailer class into the global namespace $this->getLibraryWNS('PHPMailer'); $this->getLibraryWNS('SMTP'); //Create a new PHPMailer instance $mail = new PHPMailerPHPMailerPHPMailer(); //Tell PHPMailer to use SMTP $mail->isSMTP(); //Enable SMTP debugging // $mail->SMTPDebug = 0; // 0 = off (for production use), 1 = client messages, 2 = client and server messages Godaddy POR CONFIRMAR $mail->SMTPDebug = 1; // debugging: 1 = errors and messages, 2 = messages only //Whether to use SMTP authentication $mail->SMTPAuth = true; // authentication enabled //Set the encryption system to use - ssl (deprecated) or tls $mail->SMTPSecure = 'ssl'; //Seguridad Correo Gmail //Set the hostname of the mail server $mail->Host = "smtp.gmail.com"; //Host Correo Gmail //Set the SMTP port number - 587 for authenticated TLS, a.k.a. RFC4409 SMTP submission $mail->Port = 465; //587; //Verifica si el servidor acepta envios en HTML $mail->IsHTML(true); //Username to use for SMTP authentication - use full email address for gmail $mail->Username = 'tumail@gmail.com'; //Password to use for SMTP authentication $mail->Password = 'tucontraseña'; //Set who the message is to be sent from $mail->setFrom('tumail@gmail.com','Creador de Páginas Web'); $mail->Subject = $asunto; $mail->Body = $cuerpo; //Set who the message is to be sent to $mail->addAddress($email, $nombre); //Send the message, check for errors if(!$mail->Send()) { echo "Mailer Error: " . $mail->ErrorInfo; return false; } else { echo "Message has been sent"; return true; }
stackoverflow.com
— PHPMailer is a Free Email Transfer Class for PHP, supporting SMTP and POP3, HTML messages, attachments, and more. Sends email via sendmail, PHP mail(), QMail, or directly with SMTP.
— Download PHPMailer 5.2.1.
Example, uses PHPMailer to send email via SMTP, using a GMail account.
<?php include('PHPMailer_5.2.1/class.phpmailer.php'); // Here sets data for email $from = 'name@yourdomain.com'; $from_name = 'Your name'; $to = 'whoto@domain.com'; $toname = 'Receiver Name'; $subject = 'Subject for email'; $msg = 'The email message, can contains HTML tags'; $mail = new PHPMailer(); $mail->IsSMTP(); // telling the class to use SMTP $mail->Host = "smtp.gmail.com"; // SMTP server $mail->SMTPAuth = true; // enable SMTP authentication $mail->SMTPSecure = "ssl"; // sets the prefix to the servier $mail->Host = "smtp.gmail.com"; // sets GMAIL as the SMTP server $mail->Port = 465; // set the SMTP port for the GMAIL server $mail->Username = 'name@gmail.com'; // your GMAIL account $mail->Password = 'password'; // GMAIL password $mail->SetFrom($from, $from_name); $mail->AddReplyTo($from, $from_name); $mail->Subject = $subject; $mail->MsgHTML($msg); // to send with HTML tags $mail->AddAddress($to, $toname); if(!$mail->Send()) { echo 'Mailer Error: '. $mail->ErrorInfo; } else { echo 'Message sent!'; } ?>
Example, uses PHPMailer to send email via PHP mail(), with HTML tags and attachments.
<?php include('PHPMailer_5.2.1/class.phpmailer.php'); // Here sets data for email $from = 'name@yourdomain.com'; $from_name = 'Your name'; $to = 'whoto@domain.com'; $toname = 'Receiver Name'; $subject = 'Subject for email'; $msg = 'The email message, can contains HTML tags'; $mail = new PHPMailer(); $mail->SetFrom($from, $from_name); $mail->AddReplyTo($from, $from_name); $mail->AddAddress($to, $toname); $mail->Subject = $subject; $mail->MsgHTML($msg); // to send with HTML tags // add attachments $mail->AddAttachment('attachment_file.zip'); // attachment 1 $mail->AddAttachment('attachment_file.jpg'); // attachment 2 if(!$mail->Send()) { echo 'Mailer Error: '. $mail->ErrorInfo; } else { echo 'Message sent!'; } ?>
— In the archive with PHPMailer class you’ll find more examples, and documentation.
• PHPMailer Web Site.
coursesweb.net
Добрый день!
Наверное все, кому приходилось отправлять почту из кода на PHP через SMTP, знакомы с классом PHPMailer.
В статье я расскажу о том, как можно в несколько строк кода научить PHPMailer принимать в качестве дополнительного параметра IP адрес сетевого интерфейса, с которого мы хотим осуществить отправку. Естественно, что эта возможность будет полезна только на серверах с несколькими белыми IP адресами. А в качестве небольшого дополнения мы отловим достаточно неприятного жучка из кода PHPMailer`а.
Обзор архитектуры PHPMailer
Пакет PHPMailer состоит из одноименного фронтэнда (класс PHPMailer) и нескольких классов-плагинов, реализующих возможность отправки почты по протоколу SMTP, в том числе и с предварительной аутентификацией по POP3.
Фронтэнд PHPMailer предоставляет поля и методы по установке параметров письма (localhost, return-path, AddAdress(), body, from и пр.), выбору способа отправки и способа аутентификации (SMTPSecure, SMTPAuth, IsMail(), IsSendMail(), IsSMTP() и пр.), а также метод Send().
Установив параметры письма и указав способ отправки (возможно выбрать из следующих: mail, sendmail, qmail или smtp), необходимо вызвать метод класса PHPMailer Send(), который, в свою очередь, делегирует вызов внутреннему методу, отвечающему за отправку почты тем или иным способом. Так как нас интересует именно SMTP, то далее в основном мы будем рассматривать плагин SMTP из файла class.smtp.php.
При использовании метода PHPMailer::IsSMTP() метод PHPMailer::Send() вызовет защищенный метод PHPMailer::SmtpSend($header, $body), передав ему сформированные заголовки и тело письма.
Метод PHPMailer::SmtpSend() попытается подключиться к удаленному SMTP-серверу получателя (если это уже не первая отправка письма объектом PHPMailer, то скорее всего соединение уже было установлено и этот шаг будет пропущен) и инициировать с ним стандартную SMTP-сессию (HELLO/EHLO, MAIL TO, RCPT, DATA и т.д.).
Соединение с SMTP-сервером происходит в публичном методе PHPMailer::SmtpConnect(). Так как для одного домена может быть сразу несколько MX-записей с различными приоритетами, то метод PHPMailer::SmtpConnect() попытается последовательно соединиться с каждым из SMTP-серверов, указанных при конфигурировании PHPMailer.
Жучок в коде
А теперь внимательно посмотрим на код PHPMailer::SmtpConnect():
/** * Initiates a connection to an SMTP server. * Returns false if the operation failed. * @uses SMTP * @access public * @return bool */ public function SmtpConnect() { if(is_null($this->smtp)) { $this->smtp = new SMTP(); } $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; $connection = $this->smtp->Connected(); // Retry while there is no connection try { while($index < count($hosts) && !$connection) { $hostinfo = array(); if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { $host = $hostinfo[1]; $port = $hostinfo[2]; } else { $host = $hosts[$index]; $port = $this->Port; } $tls = ($this->SMTPSecure == 'tls'); $ssl = ($this->SMTPSecure == 'ssl'); if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); $this->smtp->Hello($hello); if ($tls) { if (!$this->smtp->StartTLS()) { throw new phpmailerException($this->Lang('tls')); } //We must resend HELO after tls negotiation $this->smtp->Hello($hello); } $connection = true; if ($this->SMTPAuth) { if (!$this->smtp->Authenticate($this->Username, $this->Password)) { throw new phpmailerException($this->Lang('authenticate')); } } } $index++; if (!$connection) { throw new phpmailerException($this->Lang('connect_host')); } } } catch (phpmailerException $e) { $this->smtp->Reset(); if ($this->exceptions) { throw $e; } } return true; }
В коде $this->smtp — это объект класса-плагина SMTP.
Постараемся разобраться, что же авторы имели в виду. Для начала выполняется проверка, создан ли внутренний объект, умеющий работать с SMTP и выполняется его создание, если это первый вызов метода SmtpConnect() объекта класса PHPMailer (на самом деле еще метод PHPMailer::Close() может превратить $this->smtp в null).
Затем поле PHPMailer::Host разбивается по разделителю ‘;’ и в итоге получается массив MX-записей для домена получателя. Если в Host была всего одна запись (например, ‘smtp.yandex.ru’), то в массиве будет всего один элемент.
Далее выполняется проверка, а не подключены ли мы уже к серверу получателя. Если это первый вызов SmtpConnect(), то очевидно, что $connection будет false.
Вот мы и добрались до самого интересного. Начинается цикл по всем MX-записям, в каждой итерации которого производится попытка подключения к очередному MX. Но что будет, если выполнить в голове алгоритм этого цикла, представив, что для первой MX-записи if ($this->smtp->Connect(($ssl? ‘ssl://’:»).$host, $port, $this->Timeout)) вернула false? Окажется, что цикл бросит исключение, которое будет перехвачено уже за циклом. Т.е. все остальные MX-записи не будут проверены на доступность и мы поймаем исключение.
Но это еще не самое неприятное. PHPMailer умеет работать в двух режимах — бросать исключения, либо же тихо умирать с записью сообщения об ошибке в поле ErrorInfo. Так вот в случае использования тихого режима ($this->exceptions == false, причем это режим по умолчанию) SmtpConnect() вернет true!
В общем этот баг отнял у меня некоторое время, разработчики о нем оповещены. Я его заметил в версии 5.2.1, но и более старые версии ведут себя так же.
Прежде чем двигаться дальше, представлю свой быстрый фикс. До выхода официального исправления от разработчиков живу с ним. Уже месяц полет нормальный.
public function SmtpConnect() { if(is_null($this->smtp)) { $this->smtp = new SMTP(); } $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; $connection = $this->smtp->Connected(); // Retry while there is no connection try { while($index < count($hosts) && !$connection) { $hostinfo = array(); if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { $host = $hostinfo[1]; $port = $hostinfo[2]; } else { $host = $hosts[$index]; $port = $this->Port; } $tls = ($this->SMTPSecure == 'tls'); $ssl = ($this->SMTPSecure == 'ssl'); $bRetVal = $this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout); if ($bRetVal) { $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); $this->smtp->Hello($hello); if ($tls) { if (!$this->smtp->StartTLS()) { throw new phpmailerException($this->Lang('tls')); } //We must resend HELO after tls negotiation $this->smtp->Hello($hello); } if ($this->SMTPAuth) { if (!$this->smtp->Authenticate($this->Username, $this->Password)) { throw new phpmailerException($this->Lang('authenticate')); } } $connection = true; break; } $index++; } if (!$connection) { throw new phpmailerException($this->Lang('connect_host')); } } catch (phpmailerException $e) { $this->SetError($e->getMessage()); if ($this->smtp->Connected()) $this->smtp->Reset(); if ($this->exceptions) { throw $e; } return false; } return true; }
Расширяем PHPMailer для работы с несколькими сетевыми интерфейсами
Плагин SMTP PHPMailer`а работает с сетью через fsockopen, fputs и fgets. Если на нашей машине несколько сетевых интерфейсов, смотрящих в Интернет, fsockopen в любом случае создаст сокет на первом соединении. Нам же необходимо уметь создавать на любом.
Первая мысль, которая пришла в голову — это использовать стандартную связку классических сокетов socket_create, socket_bind, socket_connect, которая в socket_bind позволяет указать с каким сетевым интерфейсом связать сокет, указав его IP адрес. Как оказалось, мысль не совсем удачная. В результате пришлось переписать практически весь плагин PHPMailer`а SMTP, заменив в нем fputs и fgets на socket_read и socket_write, потому что fputs и fgets не умеют работать с ресурсом, созданным socket_create. Заработало, но на душе остался осадок.
Следующая мысль оказалась удачнее. Существует же функция stream_socket_client, создающая потоковый сокет, который можно благополучно читать fgets`ом! В результате, заменив всего один метод в плагине SMTP, можно научить PHPMailer отсылать почту с явным указанием сетевого интерфейса, и при этом практически не трогать код разработчиков.
Наш плагин выглядит следующим образом:
require_once 'class.smtp.php'; class SMTPX extends SMTP { public function __construct() { parent::__construct(); } public function Connect($host, $port = 0, $tval = 30, $local_ip) { // set the error val to null so there is no confusion $this->error = null; // make sure we are __not__ connected if($this->connected()) { // already connected, generate error $this->error = array("error" => "Already connected to a server"); return false; } if(empty($port)) { $port = $this->SMTP_PORT; } $opts = array( 'socket' => array( 'bindto' => "$local_ip:0", ), ); // create the context... $context = stream_context_create($opts); // connect to the smtp server $this->smtp_conn = @stream_socket_client($host.':'.$port, $errno, $errstr, $tval, // give up after ? secs STREAM_CLIENT_CONNECT, $context); // verify we connected properly if(empty($this->smtp_conn)) { $this->error = array("error" => "Failed to connect to server", "errno" => $errno, "errstr" => $errstr); if($this->do_debug >= 1) { echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '<br />'; } return false; } // SMTP server can take longer to respond, give longer timeout for first read // Windows does not have support for this timeout function if(substr(PHP_OS, 0, 3) != "WIN") socket_set_timeout($this->smtp_conn, $tval, 0); // get any announcement $announce = $this->get_lines(); if($this->do_debug >= 2) { echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '<br />'; } return true; } }
На самом деле реализация метода Connect() тоже изменилась минимально. Заменены лишь строки, создающие непосредственно сокет и в сигнатуру добавлен еще одни параметр — IP адрес сетевого интерфейса.
Чтобы использовать этот плагин, нужно расширить класс PHPMailer следующим образом:
require_once 'class.phpmailer.php'; class MultipleInterfaceMailer extends PHPMailer { /** * IP адрес сетевого интерфейса, с которого нужно * подключаться к удаленному SMTP-серверу. * Используется при работе через плагин SMTPX. * @var string */ public $Ip = ''; public function __construct($exceptions = false) { parent::__construct($exceptions); } /** * Метод для работы с плагином SMTPX. * @param string $ip IP адрес сетевого интерфейса с доступом в Интернет. */ public function IsSMTPX($ip = '') { if ('' !== $ip) $this->Ip = $ip; $this->Mailer = 'smtpx'; } protected function PostSend() { if ('smtpx' == $this->Mailer) { $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); return; } parent::PostSend(); } /** * Внесены изменения, касающиеся отправки писем с явным указанием * IP адреса сетевого интерфейса компьютера. * @param string $header The message headers * @param string $body The message body * @uses SMTP * @access protected * @return bool */ protected function SmtpSend($header, $body) { require_once $this->PluginDir . 'class.smtpx.php'; $bad_rcpt = array(); if(!$this->SmtpConnect()) { throw new phpmailerException($this->Lang('connect_host'), self::STOP_CRITICAL); } $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; if(!$this->smtp->Mail($smtp_from)) { throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); } // Attempt to send attach all recipients foreach($this->to as $to) { if (!$this->smtp->Recipient($to[0])) { $bad_rcpt[] = $to[0]; // implement call back function if it exists $isSent = 0; $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } } foreach($this->cc as $cc) { if (!$this->smtp->Recipient($cc[0])) { $bad_rcpt[] = $cc[0]; // implement call back function if it exists $isSent = 0; $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } } foreach($this->bcc as $bcc) { if (!$this->smtp->Recipient($bcc[0])) { $bad_rcpt[] = $bcc[0]; // implement call back function if it exists $isSent = 0; $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); } else { // implement call back function if it exists $isSent = 1; $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); } } if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses $badaddresses = implode(', ', $bad_rcpt); throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); } if(!$this->smtp->Data($header . $body)) { throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); } if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); } return true; } /** * Внесены изменения, расширяющие класс PHPMailer для * работы с плагином SMTPX. * @uses SMTP * @access public * @return bool */ public function SmtpConnect() { if(is_null($this->smtp) || !($this->smtp instanceof SMTPX)) { $this->smtp = new SMTPX(); } $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; $connection = $this->smtp->Connected(); // Retry while there is no connection try { while($index < count($hosts) && !$connection) { $hostinfo = array(); if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { $host = $hostinfo[1]; $port = $hostinfo[2]; } else { $host = $hosts[$index]; $port = $this->Port; } $tls = ($this->SMTPSecure == 'tls'); $ssl = ($this->SMTPSecure == 'ssl'); $bRetVal = $this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $this->Ip); if ($bRetVal) { $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); $this->smtp->Hello($hello); if ($tls) { if (!$this->smtp->StartTLS()) { throw new phpmailerException($this->Lang('tls')); } //We must resend HELO after tls negotiation $this->smtp->Hello($hello); } if ($this->SMTPAuth) { if (!$this->smtp->Authenticate($this->Username, $this->Password)) { throw new phpmailerException($this->Lang('authenticate')); } } $connection = true; break; } $index++; } if (!$connection) { throw new phpmailerException($this->Lang('connect_host')); } } catch (phpmailerException $e) { $this->SetError($e->getMessage()); if ($this->smtp->Connected()) $this->smtp->Reset(); if ($this->exceptions) { throw $e; } return false; } return true; } }
В класс MultipleInterfaceMailer добавлено новое открытое поле Ip, которое должно быть установлено строковым представлением IP адреса сетевого интерфейса, с которого мы хотим отправлять почту. Также добавлен метод IsSMTPX(), указывающий, что письма нужно отправлять с использованием нового плагина. Методы PostSend(), SmtpSend() и SmtpConnect() также переделаны для использования плагина SMTPX. При этом объекты класса MultipleInterfaceMailer можно спокойно использовать с существующим клиентским кодом, который, например, отправляет почту через sendmail или через оригинальный плагин SMTP, так как ни процедура использования, ни интерфейс класса не изменились.
Далее небольшой пример использования нового класса:
function getSmtpHostsByDomain($sRcptDomain) { if (getmxrr($sRcptDomain, $aMxRecords, $aMxWeights)) { if (count($aMxRecords) > 0) { for ($i = 0; $i < count($aMxRecords); ++$i) { $mxs[$aMxRecords[$i]] = $aMxWeights[$i]; } asort($mxs); $aSortedMxRecords = array_keys($mxs); $sResult = ''; foreach ($aSortedMxRecords as $r) { $sResult .= $r . ';'; } return $sResult; } } //Функция getmxrr возвращает только почтовые сервера, найденные в DNS, //однако, согласно RFC 2821, когда в списке нет почтовых серверов, //необходимо использовать только $sRcptDomain в качестве почтового сервера с //приоритетом 0. return $sRcptDomain; } require 'MultipleInterfaceMailer.php'; $mailer = new MultipleInterfaceMailer(true); $mailer->IsSMTPX('192.168.1.1'); //Здесь необходимо указать IP адрес желаемого интерфейса //$mailer->IsSMTP(); а можно и по старинке $mailer->Host = getSmtpHostsByDomain('email.net'); $mailer->Body = 'blah-blah'; $mailer->From ='no-replay@yourdomain.net'; $mailer->AddAddress('sucreface@email.net'); $mailer->Send();
Заключение
Подведем краткий итог:
- Исправлен баг в PHPMailer, из-за которого SmtpConnect() всегда возвращал true, даже в случае неудачной попытки подключения к SMTP-серверу.
- SmtpConnect() стал по-честному проверять все переданные ему MX-записи до первой удачной попытки.
- Написан новый плагин, с помощью которого можно отправлять почту через SMTP явно указывая какой сетевой интерфейс отправляющего сервера использовать.
- PHPMailer безболезненно для старого клиентского кода расширен для использования нового плагина SMTPX.
Удачи в ваших начинаниях, друзья!
habr.com
Is it an alternative to PHP’s mail()
function?
In most cases, it’s an alternative to PHP’s mail()
function, but there are many other cases where the mail()
function is simply not flexible enough to achieve what you need.
First of all, PHPMailer provides an object oriented interface, whereas mail()
is not object oriented. PHP developers generally hate to create $headers
strings while sending emails using the mail()
function because they require a lot of escaping – PHPMailer makes this a breeze. Developers also need to write dirty code (escaping characters, encoding and formatting) to send attachments and HTML based emails when using the mail()
function whereas PHPMailer makes this painless.
Also, the mail()
function requires a local mail server to send out emails. PHPMailer can use a non-local mail server (SMTP) if you have authentication.
Further advantages include:
- It can print various kinds of errors messages in more than 40 languages when it fails to send an email
- Integrated SMTP protocol support and authentication over SSL and TLS
- Can send alternative plaintext version of email for non-HTML email clients
- Very active developer community which keeps it secure and up to date
PHPMailer is also used by popular PHP content management systems like WordPress, Drupal, Joomla etc.
Installing PHPMailer
You can install PHPMailer using Composer:
composer require phpmailer/phpmailer
Sending Email from Local Web Server using PHPMailer
Here is the simplest example of sending an email from a local web server using PHPMailer
<?php require_once "vendor/autoload.php"; //PHPMailer Object $mail = new PHPMailer; //From email address and name $mail->From = "from@yourdomain.com"; $mail->FromName = "Full Name"; //To address and name $mail->addAddress("recepient1@example.com", "Recepient Name"); $mail->addAddress("recepient1@example.com"); //Recipient name is optional //Address to which recipient will reply $mail->addReplyTo("reply@yourdomain.com", "Reply"); //CC and BCC $mail->addCC("cc@example.com"); $mail->addBCC("bcc@example.com"); //Send HTML or Plain Text email $mail->isHTML(true); $mail->Subject = "Subject Text"; $mail->Body = "<i>Mail body in HTML</i>"; $mail->AltBody = "This is the plain text version of the email content"; if(!$mail->send()) { echo "Mailer Error: " . $mail->ErrorInfo; } else { echo "Message has been sent successfully"; }
The code and comments should be sufficiently clear to explain everything that’s going on.
Sending an E-Mail with Attachments
Let’s see an example on how to send an email with attachments using PHPMailer.
<?php require_once "vendor/autoload.php"; $mail = new PHPMailer; $mail->From = "from@yourdomain.com"; $mail->FromName = "Full Name"; $mail->addAddress("recipient1@example.com", "Recipient Name"); //Provide file path and name of the attachments $mail->addAttachment("file.txt", "File.txt"); $mail->addAttachment("images/profile.png"); //Filename is optional $mail->isHTML(true); $mail->Subject = "Subject Text"; $mail->Body = "<i>Mail body in HTML</i>"; $mail->AltBody = "This is the plain text version of the email content"; if(!$mail->send()) { echo "Mailer Error: " . $mail->ErrorInfo; } else { echo "Message has been sent successfully"; }
Here we are attaching two files i.e., file.txt
which resides in the same directory as the script and images/profile.png
which resides in images
directory of the script directory.
To add attachments to the email we just need to call the function addAttachment
of the PHPMailer object by passing the file path as argument. For attaching multiple files we need to call it multiple times.
Using SMTP
You can use the mail server of an another host to send email, but for this you first need to have authentication. For example: to send an email from Gmail’s mail server you need to have a Gmail account.
SMTP is a protocol used by mail clients to send an email send request to a mail server. Once the mail server verifies the email it sends it to the destination mail server.
Here is an example of sending an email from Gmail’s mail server from your domain. You don’t need a local mail server to run the code. We will be using the SMTP protocol:
<?php require_once "vendor/autoload.php"; $mail = new PHPMailer; //Enable SMTP debugging. $mail->SMTPDebug = 3; //Set PHPMailer to use SMTP. $mail->isSMTP(); //Set SMTP host name $mail->Host = "smtp.gmail.com"; //Set this to true if SMTP host requires authentication to send email $mail->SMTPAuth = true; //Provide username and password $mail->Username = "name@gmail.com"; $mail->Password = "super_secret_password"; //If SMTP requires TLS encryption then set it $mail->SMTPSecure = "tls"; //Set TCP port to connect to $mail->Port = 587; $mail->From = "name@gmail.com"; $mail->FromName = "Full Name"; $mail->addAddress("name@example.com", "Recepient Name"); $mail->isHTML(true); $mail->Subject = "Subject Text"; $mail->Body = "<i>Mail body in HTML</i>"; $mail->AltBody = "This is the plain text version of the email content"; if(!$mail->send()) { echo "Mailer Error: " . $mail->ErrorInfo; } else { echo "Message has been sent successfully"; }
Gmail requires TLS encryption over SMTP so we set it accordingly. Before you send via SMTP, you need to find out the host name, port number, encryption type if required and if authentication is required you also need the username and password. Note that having a two-factor authentication enabled on Gmail won’t let you use their SMTP with username/password – instead, additional configuration will be required.
One big advantage in using remote SMTP over local mail is that if you use PHP’s mail()
function to send email with the from
address domain set to anything other than the local domain name (name of the server), then the recipient’s email server’s attack filters will mark it as spam. For example, if you send an email from a server with actual host name example.com
with the from
address name@gmail.com
to name@yahoo.com
, then Yahoo’s servers will mark it as spam or display a message to the user not to trust the email because the mail’s origin is example.com
and yet it presents itself as if coming from gmail.com
. Although you own name@gmail.com
, there is no way for Yahoo to find that out.
Retrieving E-Mails using POP3
PHPMailer also allows POP-before-SMTP verification to send emails. In other words, you can authenticate using POP and send email using SMTP. Sadly, PHPMailer doesn’t support retrieving emails from mail servers using the POP3 protocol. It’s limited to only sending emails.
Displaying Localized Error Messages
$mail->ErrorInfo
can return error messages in 43 different languages.
To display error messages in a different language, copy the language
directory from PHPMailer’s source code to the project directory.
To return error messages in, for example, Russian, set the PHPMailer object to the Russian language using the below method call:
$mail->setLanguage("ru");
You can also add your own language files to the language
directory.
Conclusion
If you are a PHP developer, there is little chance of avoiding having to send emails programmatically. While you may opt for third party services like Mandrill or Sendgrid, sometimes that just isn’t an option, and rolling your own email sending library even less so. That’s where PHPMailer and its alternatives (Zend Mail, Swiftmailer, etc..) come in.
You can learn about this library’s APIs in the official documentation. Do you use PHPMailer? Or do you rather rely on fully remote API based solutions? Let us know in the comments!
Are you getting bogged down with PHP library dependencies? Watch our screencast and learn about how Composer can help you manage this for you.
www.sitepoint.com