Простой Способ Поставить Электронную Почту В Очередь С Помощью Zend_Mail

Всем привет. Я хочу поделиться очень простым и легким способом организовать очередь электронной почты с помощью Zend_Mail. Примеры в статье намеренно сделаны максимально простыми и не привязаны к фреймворку, т.к.

цель статьи показать метод, а не конкретную реализацию.

Кроме того, это решение необязательно использовать только внутри Zend Framework; он может легко вписаться в любой проект. Прямо в точку.

Нам понадобится: - таблица в базе данных; — транспортный класс; — файл, реализующий отправку сообщений (запускается через cron).

Сначала давайте определим таблицу базы данных:

  
  
  
   

CREATE TABLE email_queue ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, recipients TEXT NOT NULL, subject CHAR(255) NOT NULL, message TEXT NOT NULL, header TEXT NOT NULL, parameters TEXT, max_attempts TINYINT UNSIGNED NOT NULL DEFAULT 3, attempts TINYINT UNSIGNED NOT NULL DEFAULT 0, is_false TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, in_process INT UNSIGNED DEFAULT NULL DEFAULT 0, time_last_attempt INT UNSIGNED DEFAULT NULL, create_time INT UNSIGNED NOT NULL );

Далее давайте создадим наш транспортный класс:

class EmailQueueTransport extends Zend_Mail_Transport_Sendmail { /** * Send mail using EmailQueue * * @access public * @return void * @throws Zend_Mail_Transport_Exception If parameters is set but not a string * @throws Zend_Mail_Transport_Exception If failed to add a message in queue */ public function _sendMail() { if ($this->parameters !== null && !is_string($this->parameters)) { /** * Exception is thrown here because $parameters is a public property */ throw new Zend_Mail_Transport_Exception('Parameters were set but are not a string'); } $db = Zend_Db_Table_Abstract::getDefaultAdapter(); $statement = $db->prepare(' INSERT email_queue SET recipients = :recipients, subject = :subject, message = :message, header = :header, parameters = :parameters, create_time = :create_time '); $result = $statement->execute(array( 'recipients' => $this->recipients, 'subject' => $this->_mail->getSubject(), 'message' => $this->body, 'header' => $this->header, 'parameters' => $this->parameters, 'create_time' => time() )); if (!$result) { throw new Zend_Mail_Transport_Exception( 'Failed to add a message in queue.'); } } }

Все, что делает этот класс, — это получает уже подготовленные данные от Zend_Mail и сохраняет их в базе данных.

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

<Эphp /** * Add the messages in the log * * @param type $message * @return void */ function set_log($message) { $message = date('H:i d.m.Y ', time()) .

$message .

"\r\n"; $logFile = dirname(__FILE__) .

DIRECTORY_SEPARATOR .

'logs' .

DIRECTORY_SEPARATOR .

basename(__FILE__, '.

php') .

'.

log'; error_log($message, 3, $logFile); } try { $config = include realpath(dirname(__FILE__) .

'/.

/') .

'/application/config/config.php'; // Path to config file $configDb = $config['db']['params']; $db = new PDO( 'mysql:host=' .

$configDb['host'] .

';dbname=' .

$configDb['dbname'], $configDb['username'], $configDb['password'], array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'", 'profiler' => false, ) ); } catch (PDOException $e) { set_log('Connection error: ' .

$e->getMessage()); } $limit = 100; // limit rows $processLimitTime = 600; // 10 minutes $statement = $db->query(' SELECT * FROM email_queue WHERE attempts < max_attempts AND in_process < ' .

(time() - $processLimitTime) .

' ORDER BY id ASC LIMIT ' .

$limit ); $rows = $statement->fetchAll(PDO::FETCH_ASSOC); foreach ($rows as $row) { $db->beginTransaction(); $result = $db->exec(' UPDATE email_queue SET in_process = ' .

time() .

' WHERE id = ' .

$row['id'] ); if (!$result) { set_log('Error when updating record from the table "email_queue".

Id queue is ' .

$row['id'] .

'.

'); continue; } $isSent = mail( $row['recipients'], $row['subject'], $row['message'], $row['header'], $row['parameters'] ); $db->commit(); if ($isSent) { $result = $db->exec('DELETE from email_queue WHERE id = ' .

$row['id']); if (!$result) { set_log('Error when deleting record from the table "email_queue".

Id queue is ' .

$row['id'] .

'.

'); } } else { $result = $db->exec(' UPDATE email_queue SET is_false = 1, in_process = 0, attempts = ' .

($row['attempts'] + 1) .

' WHERE id = ' .

$row['id'] ); if (!$result) { set_log('Error when updating record from the table "email_queue".

Id queue is ' .

$row['id'] .

'.

'); } set_log('Error when sending messages to e-mail. Id queue is ' .

$row['id'] .

', e-mails is ' .

$row['recipients'] .

'.

'); } }

Использование очереди:

$mail = new Zend_Mail(); $mail->setFrom($from); $mail->addTo($to); $mail->setBodyHtml($body); $mail->setSubject($subject); $transport = new \EmailQueueTransport(); $mail->send($transport);

Соответственно, если вам нужно отправить сообщение без очереди, через mail(), не передавайте транспорт или передайте null. P.S. Возможно, кто-то посчитает более правильным использовать Zend_Queue и, возможно, будет прав.

Я ни в коем случае не утверждаю, что метод, описанный в статье, является единственно правильным решением.

Решений всегда много, я описал лишь одно из них.

П.

П.

С.

Особая благодарность пользователям за их содержательные комментарии.

МаркПнк И ген .

Теги: #zend #zend_mail #php #очередь сообщений #электронная почта #Zend Framework

Вместе с данным постом часто просматривают: