⬆️ ⬇️

A simple way to organize an e-mail queue with Zend_Mail

Hello.



I want to share a very simple and easy way to organize an e-mail queue using Zend_Mail. The examples in the article are intentionally made as simple as possible and are not tied to the framework, since the purpose of the article is to show the way, not the concrete implementation. In addition, this solution does not necessarily have to be used only within the Zend Framework, it will easily fit into any project.



Immediately to the point. We will need:

- table in the database;

- class of transport;

- a file that implements sending messages (runs on the crown).



First we define the database table:

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 ); 


')

Next, create a class of our transport:

 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.'); } } } 


All that this class does is get the already prepared data from Zend_Mail and save it to the database. If creating an entry in the table failed, throws a Zend_Mail_Transport_Exception exception.



Well, the file that implements sending messages, suppose that the file is in a separate folder, in the application root:

 <?php /** * Add the messages in the log * * @param type $message * @return void */ function set_log($message) { $message = date('H:i dmY ', 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'] . '.'); } } 


Use Queue:

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


Accordingly, if you need to send a message out of turn, via mail () - do not pass the transport or pass null.



PS Perhaps someone will find it more correct to use Zend_Queue, and it may be right. I am by no means arguing that the method described in the article is the only correct solution. There are always many solutions, I have described only one of them.



PPS Special thanks for the useful comments from markPnk and gen users.

Source: https://habr.com/ru/post/150716/



All Articles