Поиск по этому блогу

вторник, 20 декабря 2011 г.

Размышления о правильной отправке почты в вашем PHP приложении или Swift Mail изнутри

Привет, %username%.

Если ты использовал Symfony 2.0, то наверняка некоторые фичи захотелось бы вытащить и использовать в другом, не symfony2, проекте. Сегодня мы поговорим об отправке почты.




Краткая справка как правильно. 

Как известно, PHP уже имеет встроенную функцию отправки почты. Она использует sendmail. И в принципе, там почти всё есть. Почему же появляются разные библиотеки, типа Zend_Mail или Swift_Mail ?

ИХМО, ответ прост:
отправка сообщений, это не только непосредственная отправка данных почтовому серверу, а так же ещё комплекс мероприятий по формированию данных для отправки. 

Общий алгоритм отправки сообщения:

  1. Формируем данные(текст или HTML), заголовки(тема и прочие параметры), вложенные файлы.
  2. отправляем их mail-серверу
  3. если отправка по каким-либо причинам не удалась, то через некоторое время пункт 2 следует повторить

Отметим, что пункт 3 просто не обходим, т.к на момент отправки могут быть проблемы с интернетом, или с сервером. Исходя из описанной стратегии, очевидно, что пункты 2-3 нужно выполнять в фоне.

У нас есть функция, которая делает пункт 2. И только! А остальную логику писать надо самим. Плюс не слишком удобный процедурный синтаксис заставляет нас делать одни и те же действия постоянно.

Чего ещё не хватает? 

  • реализации пункта 2-3 алгоритма отправки в одной логике
  • SMTP-протокол
  • Вложения более простым способом 
  • И прочих фишек и плюшек, далее перечислять лень, всё-равно что-нибудь забуду... 


Общий алгоритм действий


  1. Формируем данные и кладем их в очередь
  2. Пускаем крон (процесс-задание, выполняемое периодически), который 
    1. вытаскивает пачку данных из очереди (например, 10 писем, будем слать по N штук за раз)
    2. отсылает их на mail сервер
    3. в случае успеха удаляет их из очереди
    4. выходит, чтобы запуститься через некоторое время снова.


Что предлагают нам сторонние библиотеки? 
Как всегда, не очень хочется изобретать велосипед. Причем, по началу у всех они кривы на столько, что хочется выкинуть и переписать заново.

Zend, конечно, предлагает  достаточно сильный вариант, но логику 3 придется делать самим, тем более механизм для этой логики они тоже дают: очереди.

ИХМО, Zend - это библиотека-конструктор, она дает много мелких кубиков, из которых можно собрать что угодно. Надо всего лишь только собрать. Так и тут, даны очереди и функция отправки почты, остальное господа разработчики напишите сами.
Скажу, честно: Zend мне не очень нравится, т.к. его кубики слишком ароматны, их сложно наследовать и расширять. Местами без методики COPY-PASTE обойтись практически не возможно. Часто приходится делать практически одно и тоже из проекта в проект.
Если же вы разрабатываете на Zend, то вы обречены на ковыряние с кубиками разной формы. Местами у меня складывается впечатление, что у разработчиков Zend туговато с принципами ООП и с пониманием, что такое фреймворк.

И так кубики, от Zend:

  • Делаем кубик с данными (все данные необходимые для отправки)
  • Кладем в его в очередь 
  • В кроне:
    • достаем N кубиков из очереди (Zend_Queue)
    • формируем для каждого из них письмо через Zend_Mail
    • Отправляем, если успешно, удаляем из очереди


Другой популярный фреймворк: Symfony 2 предлагает использовать удобную библиотеку swift_mail. Эта же библиотека предлагает решение-очередь. Повторяться как его делать я не буду, т.к. в документации, всё подробно описано.

Решение swift_mail более привлекательно потому, что кубика с данными (как в случае Zend) нам уже не нужно, его функцию выполняет само письмо, положенное в spool. Таким образом удается убрать лишний слой абстракции и упростить немного конечное приложение. А как известно, чем проще код, тем лучше.

В реализации отправки почты в symfony2, мы просто пишем код создания письма и прописываем команду в crontab. Я как разработчик должен закодировать формирование данных письма, а остальную рутинную работу за меня делает фреймворк.
И это приятно.

Swift Mail изнутри
Если хочется, иметь swift_mail в вашем другом проекте, то библиотеку SwiftMail можно использовать отдельно. Давайте посмотрим на эту библиотеку изнутри.

Ставим её при помощи PEAR, или качаем c github, или вообще грузим архив с официального сайта.
Библиотека написана с использованием PHP 5.2. Это замечательно, т.к. не всегда есть возможность использовать PHP 5.3. Документация и код сайта говорит о двух основных уровнях абстракции:

  • Первый - транспорт. Это группа объектов, объединенных интерфейсом, которые позволяют определить различные настройки отправки почты. за подробностями отошлю к документации (cм. заголовок - Transport).
  • Второй - почтовик. Это объект, который предоставляет API по созданию любых почтовых сообщений.


Очереди почтовых сообщений
Такая функциональность как очередь почтовых сообщений на отправку реализована как подтип  транспорта. Для продакшена рекомендую использовать Swift_FileSpool. Для тестирования логики приложения, лучше использовать  Swift_MemorySpool, которая в свою очередь обертывает простой массив. 
Пример отправки почты через очереди


Скрипт для отправки сообщений в очередь сообщений. 


<?php
//require_once 'lib/swift_init.php';
// тут аутолоадер свой подключается. 
require_once 'swift_required.php';

//Create the Transport
$spool = new Swift_FileSpool(realpath(dirname(__FILE__).'/../spool'));
$transport = Swift_SpoolTransport::newInstance($spool);

//Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);

$message = Swift_Message::newInstance('Тест!','Тест-тест!','txt/text','cp1251')
  ->setFrom(array('test1@mail.ru' => 'Васька Пупкин'))
  ->setTo(array('test2@mail.ru' => 'Петька', 'test3@mail.ru' => 'Петрович'))
  ;

//Send the message
$numSent = $mailer->send($message);


Обратите внимание, создаем очередь. Подпихиваем её в специальный транспорт, который понимает интерфейс очередей и шлет в очередь данные. А далее как по маслу: через API шлем почту, а почта складывается в очереди.


А теперь рассмотрим скрипт -крон непосредственной отправки писем:


<?php
$messageLimit = 10;
$timeLimit = 0;
require_once 'swift_required.php';

$spool = new Swift_FileSpool(realpath(dirname(__FILE__).'/spool'));
$transportReal = Swift_SmtpTransport::newInstance('smtp.mail.ru', 25)
    ->setUsername('robot@mail.ru')
    ->setPassword('pass');

$spool->setMessageLimit($messageLimit);
$spool->setTimeLimit($timeLimit);
$sent = $spool->flushQueue($transportReal);

echo sprintf('sent %s emails', $sent);
?>

Здесь мы делаем два транспорта, один очередь, а другой реальный. И по 10 штук шлем из одного в другой.


На этом всё. Надеюсь, я кому-нибудь помог. Всем спасибо за внимание..






Комментариев нет:

Отправить комментарий