Details
-
Type:
Patch
-
Status:
Open
-
Priority:
Minor
-
Resolution: Unresolved
-
Affects Version/s: 1.11.0
-
Fix Version/s: None
-
Component/s: Zend_Mail
-
Labels:None
Description
Patch overview
The attached patch adds two capabilities to Zend_Mail_Protocol_Smtp:
(1) Option to enable SMTP pipelining (rfc 2920). This greatly speeds up SMTP delivery on high-latency connections or when delivering many emails or recipients as there are less round-trips waiting for server responses. (To enable, use constructor $config option pipelining=true).
$transport = new Zend_Mail_Protocol_Smtp('smtp.domain.com', array('pipelining' => true));
(2) Option to suppress exceptions on RCPT commands. Useful if you want a message to proceed to send even if one or more recipients are rejected by the server when the RCPT command is issued (use constructor $config option throwRcptExceptions=false). Retrieve RCPT exceptions via getRcptExceptions() – returns an array where the keys are the failed recipient address and the values are the stored exceptions – use $exception->getMessage() to read each SMTP response.
$config = array('throwRcptExceptions' => false);
$transport = new Zend_Mail_Transport_Smtp('smtphost', $config));
$mail = new Zend_Mail;
$mail->setfrom('foo@bar');
$mail->setSubject('foo');
$mail->setBodyText('foo');
$mail->addTo('invalid@domain.com', 'foo'); /* would normally throw exception */
$mail->addTo('valid@domain.com', 'foo');
$transport->send($mail);
// iterate through rcpt exceptions
foreach ($transport->getConnection()->getRcptExceptions() as $key => $exception) {
echo sprintf('Failed to send to %s - server responded "%s"', $key, $exception->getMessage());
}
// get list of failed recipients
$exceptions = $transport->getConnection()->getRcptExceptions();
$failedRecipients = array_keys($exceptions);
// get count of failed and successful recipients
$numFailed = count($exceptions);
$numSuccessful = count($mail->getRecipients()) - $numFailed;
Internals
SMTP pipelining allows batches of commands (as implemented here, MAIL FROM, RCPT TO, and RSET) to be sent without waiting for server response. This has been implemented in Zend_Mail_Protocol_Smtp by having the _expect() function queue expected server responses until a non-pipelining command (i.e. DATA) is issued, at which point all queued server responses are processed in sequence, evaluated against the expected responses.
Unfortunately there is currently very thin unit tests for the SMTP components of Zend_Mail. As the parent class, Zend_Mail_Protocol_Abstract operates directly on sockets which is a necessity to facilitate TLS encryption features, I do not see an easy way to mock up a test adapter to use for testing. Any suggestions about how to do this to allow better testing of the new functionality would be appreciated. Came up with a way to mock the socket connection for unit testing; now awaiting commit of ZF-10741 and will then add unit tests to this patch
The patch also adds internals to facilitate future improvements related to SMTP extensions. EHLO response (the list of server-supported SMTP extensions) is now parsed and can be queried with $this->_supports(). So, future enhancements could include parsing of ENHANCEDSTATUSCODES (rfc 2034), SIZE (rfc 1870), etc.
Recommend applying (
ZF-8511) before this patch (ZF-8528) asZF-8511resolves an issue with Zend_Mail not clearing the receive buffer when throwing an exception. ZF-8528 needs this ability as it internally uses a try/catch block to continue processing after an RCPT error when throwRcptExceptions config option is set to false.ZF-8511) before this patch (ZF-8528) asZF-8511resolves an issue with Zend_Mail not clearing the receive buffer when throwing an exception. ZF-8528 needs this ability as it internally uses a try/catch block to continue processing after an RCPT error when throwRcptExceptions config option is set to false.