morontt.info

Zend Framework → Отправка писем в спул

Под отправкой писем в спул, я подразумеваю именно стратегию, когда реальная отправка письма происходит в фоновом процессе, а не в том, который отправку письма инициализировал. Поясню на примере данного блога. Некто отправляет комментарий, отвечая на комментарий другого пользователя. Этим действием запускается механизм, который отправляет письмо мне, т.е. хозяину блога, о том, что кто-то что-то написал. Дальше отправляется письмо тому, на чей комментарий ответили. Ну и, допустим, что письма о комментарии отправляются всем, кто подписался на комментарии к конкретному топику блога. Так вот зачем нашему некто ожидать, пока всем письма отправятся? В случае же массовых рассылок, когда адресатов сотни и тысячи, по другому и нельзя, по моему.

Решением данной ситуации и будет отправка писем в спул, т.е. помещение их в некую очередь, откуда они будут извлекаться фоновым скриптом, и рассылаться адресатам.

Не смотря на то, что запись эта относится к категории Zend Framework, я использовал стороннюю библиотеку, Swiftmailer, которую можно взять с официального сайта. Конечно, и в зенде существуют компоненты, позволяющие решить данную задачу без посторонней помощи, это Zend_Mail и Zend_Queue, но почему бы не воспользоваться готовым решением, отлаженным и без явных ошибок?

Для начала создаём метод, отправляющий письма в спул.

    public function sendEmailToSpool($recipientEmail, $recipientName, $subject, $mailBody)
    {
        //подключаем swift mailer
        $pathToSwiftmailer = realpath(APPLICATION_PATH . '/../path/to/swift_required.php');
        require_once($pathToSwiftmailer);

        $message = Swift_Message::newInstance();
        $message->setFrom('from@example.org')
                ->setContentType('text/html');

        //создаём транспорт для спула
        $spoolPath = realpath(APPLICATION_PATH . '/../path/to/spool');
        $spool     = new Swift_FileSpool($spoolPath);

        $emailValidator = new My_EmailValidator();
        if ($emailValidator->isValid($recipientEmail)) {

            $message->setSubject($subject)
                    ->setTo(array($recipientEmail => $recipientName))
                    ->setBody($mailBody);

            //отправляем сообщение в очередь
            $spool->queueMessage($message);
        }
    }

В коде можно встретить нестандартный валидатор для писем. Появление его обусловлено тем, что не все письма, валидные по мнению Zend_Validate_EmailAddress, являются валидными для свифт-мейлера. Отправка таких неоднозначных емейлов приводит к возникновению исключений, которые нам не нужны. Ниже код валидатора, где опять скрещиваются зенд и свифт-мейлер.

class My_EmailValidator extends Zend_Validate_EmailAddress
{

    public function isValid($value)
    {
        if (parent::isValid($value)) {
            $pathToSwiftmailer = realpath(APPLICATION_PATH . '/../path/to/swift_required.php');
            require_once($pathToSwiftmailer);

            $swiftGrammarObject = new Swift_Mime_Grammar();
            $addressDefinition  = $swiftGrammarObject->getDefinition('addr-spec');

            if (!preg_match('/^' . $addressDefinition . '$/D', $value)) {
                $this->_error(self::INVALID);
                return false;
            }

            return true;
        } else {
            $this->_error(self::INVALID);
            return false;
        }
    }
}

Ну и последнее. Пишем cron-скрипт, который и будет отправлять письма. В качестве транспорта выбран SMTP, но это не принципиально, может быть любой другой. Параметры для SMTP будут подтягиваться из файла application.ini, чтобы можно было менять их централизованно.

//application.ini
[production]

//прочие параметры

mail_smtp.host = "smtp.host"
mail_smtp.port = "25"
mail_smtp.username = "noreply@example.com"
mail_smtp.password = "0000"
mail_smtp.from = "noreply@example.com"

Ну и сам скрипт. Учтите, что пути к файлам и папкам могут быть и другие.

//подключаем пути к библиотекам
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(dirname(__FILE__) . '/../library'),
    get_include_path(),
)));

//извлекаем конфигурацию SMTP
require_once(realpath(dirname(__FILE__) . '/../library/Zend/Config/Ini.php'));
$configObject = new Zend_Config_Ini(realpath(dirname(__FILE__) . '/../path/to/application.ini'),
    'production');
$optionsArray = $configObject->mail_smtp->toArray();

$pathToSwiftmailer = realpath(dirname(__FILE__) . '/../path/to/swift_required.php');
require_once($pathToSwiftmailer);

//создаём спул
$spoolPath = realpath(dirname(__FILE__) . '/../path/to/spool');
$spool = new Swift_FileSpool($spoolPath);
$spool->setMessageLimit(20);
$spool->setTimeLimit(30);

//создаём SMTP-transport
$transport = Swift_SmtpTransport::newInstance();
$transport->setHost($optionsArray['host'])
          ->setPort($optionsArray['port'])
          ->setUsername($optionsArray['username'])
          ->setPassword($optionsArray['password']);

$spool->flushQueue($transport);

Вот, собственно, и всё.

g-plus-icon
comments powered by Disqus
Учтите, что комментарии добавляются только при включённом JavaScript в браузере. В ином случае информация хоть и не пропадёт бесследно, но будет отправлена в спам и вряд ли хоть кто-нибудь её увидит.
Iran
avatar
Shoot, so that's that one supopess.
Ответить
1 комментарий Написать что-нибудь
Поля, помеченные asterisk, обязательны для заполнения.
Адрес электронной почты нигде не отображается, необходим только для обратной связи.
Веб-сайт вводите в формате http://example.org, при желании, конечно.