Всем привет.
Хочу поделиться способом организации e-mail очереди с помощью Zend_Mail.
Сразу к делу. Нам потребуется:
— таблица в БД;
— класс транспорта EmailTransport;
— файл реализующий отправку сообщений (запускается по крону).
Сперва определим таблицу БД:
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,
time_last_attempt INT UNSIGNED DEFAULT NULL,
create_time INT UNSIGNED NOT NULL
);
Далее, создадим класс нашего транспорта:
class EmailQueueTransport extends ZendMailTransportSendmail
{
/**
* Send mail using EmailQueue
*
* @access public
* @return void
* @throws ZendMailTransportExceptionRuntimeException If parameters is set but not a string
* @throws ZendMailTransportExceptionRuntimeException 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 ZendMailTransportExceptionRuntimeException(
'Parameters were set but are not a string');
}
$db = ZendDbTableAbstractTable::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 ZendMailTransportExceptionRuntimeException(
'Failed to add a message in queue.');
}
}
}
Всё что делает этот класс, это — получает уже подготовленные данные из Zend_Mail и сохраняет их в БД. Eсли создать запись в таблице не удалось, выбрасывает исключение RuntimeException.
Ну и файл реализующий отправку сообщений:
<?php
...
// Подключение к БД, увеличение лимита времени исполнения, памяти и т.д.
...
$limit = 100;
$result = mysql_query('SELECT * FROM email_queue WHERE attempts <= max_attempts LIMIT ' . $limit);
$num_results = mysql_num_rows($result);
if ($num_results > 0) {
for ($i = 0; $i < $num_results; $i++) {
$row = mysql_fetch_assoc($result);
$isSent = mail(
$row['recipients'],
$row['subject'],
$row['message'],
$row['header'],
$row['parameters']
);
if ($isSent) {
mysql_query('DELETE from email_queue WHERE id = ' . $row['id']);
if (!mysql_affected_rows()) {
echo 'Error when deleting record from the table "email_queue". Id queue is ' . $row['id'] . '.';
}
} else {
mysql_query('
UPDATE email_queue
SET is_false = 1,
attempts = ' . $row['attempts'] + 1 . '
WHERE id = ' . $row['id']
);
if (!mysql_affected_rows()) {
echo 'Error when updating record from the table "email_queue". Id queue is ' . $row['id'] . '.';
}
echo 'Error when sending messages to e-mail. Id queue is ' . $row['id'] . ', e-mails is ' . $row['recipients'] . '.';
}
}
}
Использование очереди:
$mail = new ZendMailMail();
$mail->setFrom($from);
$mail->addTo($to);
$mail->setBodyHtml($body);
$mail->setSubject($subject);
$transport = new EmailQueueTransport();
$mail->send($transport);
Соответственно, если нужно отправить сообщение без очереди, через mail() — не передайте транспорт либо передайте null.
P.S. В примерах я использовал Zend Framework 2.0, которая на момент написания статьи ещё находится на стадии бета-тестирования. Если вы работаете с версией 1.*, то нужно переименовать классы соответствующим образом: ZendMailMail -> Zend_Mail,
ZendMailTransportSendmail -> Zend_Mail_Transport_Sendmail,
ZendMailTransportExceptionRuntimeException -> Zend_Mail_Transport_Exception,
ZendDbTableAbstractTable -> Zend_Db_Table_Abstract
Автор: Nilov_A