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

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

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

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, являются валидными для свифт-мейлера. Отправка таких неоднозначных емейлов приводит к возникновению исключений, которые нам не нужны. Ниже код валидатора, где опять скрещиваются зенд и свифт-мейлер.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

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, чтобы можно было менять их централизованно.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//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"

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

//подключаем пути к библиотекам
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);

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

Комментарии

avatar
Iran
avatar
Shoot, so that's that one supopess.
ответить
1 комментарий Написать что-нибудь
Или войдите, чтобы не заполнять форму:
Адрес электронной почты нигде не отображается, необходим только для обратной связи.
Напрограммировано на Go 1.23.2, версия движка 589441d