Всем привет. Я хочу поделиться очень простым и легким способом организовать очередь электронной почты с помощью 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
-
Способы Уничтожения Игровых Интерфейсов
19 Oct, 24 -
5 Фактов О Crm, Которые Вас Разочаруют
19 Oct, 24