In a project, we've discovered some strange behavior of Zend\Mail
while trying to send a single message to multiple bcc
recipients. If addressed multiple bcc
(or even cc
) recipients, only the first one would get the message delivered by the mail server. It took us quite a long time to figure it out and found a solution.
Well, to understand what Zend-Mail
does in background let's assume a simple Message
object, like this:
use Zend\Mail;
$mail = new Mail\Message();
$mail->setFrom('test@example.com', 'Sender\'s name');
$mail->addTo('daniel@example.com', 'Name of recipient');
$mail->setSubject('Test Subject');
$mail->setBody('This is the text of the email.');
$mail->setBcc([
'bcc_recipient_one@example.com',
'bcc_recipient_two@example.com',
'bcc_recipient_three@example.com',
]);
$transport = new Mail\Transport\Sendmail();
$transport->send($mail);
Zend will create a Message
object and build the Headers
by the provided information. If you would print them by $mail->getHeaders()->toString()
, the result would look somehow like this:
Date: Wed, 17 Jan 2018 14:04:14 +0100
From: Sender's Name <test@example.com>
To: daniel@example.com
Subject: Test Subject
Bcc: bcc_recipient_one@example.com,
bcc_recipient_two@example.com,
bcc_recipient_three@example.com
The problem is, that each bcc
recipient stands in a separate line. This is the reason why our mail servers only addressed the first recipient of the list and ignored all others. We've tested this behavior with a Microsoft Exchange and two Linux servers. Each has ignored the additional recipients.
So, to solve this issue, it's needed to generate a proper mail header by Zend\Mail
. The real problem is, that Zend uses an abstract class for the generation of this and there is no way to change the generation of the header while build. It's hard-coded in there, at least until version 2.4.13. The only way (for now) is to create an own header class, used for bcc
representation, passed to the Massage
object later.
For this, I've created a new Header\Bcc
class in my modules src
folder and extended it by the original \Zend\Mail\Header\Bcc
class. Inside of the Zend\Mail\Header\AbstractAddressList
class there is a function, called getFieldValue
. This function is used to generate the header value as a string
. It would be possible to copy the whole function and change only the wrong part there. But as the function uses the Headers::FOLDING
constant to build the header, it's even possibile to use this for remove of the line-breaks from the result. This causes less code and this solution would even work on changes by updates in the future.
namespace MyModule\Header;
use Zend\Mail\Header\Bcc as BccHeader;
use Zend\Mail\Header\HeaderInterface;
use Zend\Mail\Headers;
class Bcc extends BccHeader
{
/**
* get compatible bcc header
* @param bool $format
* @return string
*/
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
{
$value = parent::getFieldValue($format);
return str_replace(Headers::FOLDING, ' ', $value);
}
}
The only thing to be changed from now on, is the way the bcc
recipients will be added to the Message
object. Instead of using setBcc
or addBcc
functions, your new Bcc
class will be used and passed to the headers manually.
$bccHeader = new \MyModule\Header\Bcc();
$bccHeader->getAddressList()->addMany([
'bcc_recipient_one@example.com',
'bcc_recipient_two@example.com',
'bcc_recipient_three@example.com',
]);
$mail->getHeaders()->addHeader($bccHeader);
Once done, the headers will be correct and all recipients will get a copy of the mail. The same thing could be done with cc
recipients, which have the same problem by default.
Caution: If you use $mail->setBcc()
somewhere later in your project, the Bcc
class will be removed from the Headers
object and replaced by the original implementation. This causes the problem to stay there as before. Just remove all such function calls and use your own class!
Kommentare 0