From ce8c7e80e38d5fa6e56d6cbc6b40091aaee39b1b Mon Sep 17 00:00:00 2001 From: ADmad Date: Sun, 25 Aug 2019 02:00:47 +0530 Subject: [PATCH] Add method to Message for getting headers as string. --- src/Mailer/AbstractTransport.php | 31 +------ src/Mailer/Message.php | 39 +++++++- src/Mailer/Transport/DebugTransport.php | 3 +- src/Mailer/Transport/MailTransport.php | 33 +++---- src/Mailer/Transport/SmtpTransport.php | 11 ++- tests/TestCase/Mailer/MessageTest.php | 113 ++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 48 deletions(-) diff --git a/src/Mailer/AbstractTransport.php b/src/Mailer/AbstractTransport.php index 2b4908d84b0..df063251a45 100644 --- a/src/Mailer/AbstractTransport.php +++ b/src/Mailer/AbstractTransport.php @@ -64,33 +64,10 @@ protected function checkRecipient(Message $message): void && $message->getCc() === [] && $message->getBcc() === [] ) { - throw new Exception('You must specify at least one recipient. Use one of `setTo`, `setCc` or `setBcc` to define a recipient.'); + throw new Exception( + 'You must specify at least one recipient.' + . ' Use one of `setTo`, `setCc` or `setBcc` to define a recipient.' + ); } } - - /** - * Help to convert headers in string - * - * @param array $headers Headers in format key => value - * @param string $eol End of line string. - * @return string - */ - protected function _headersToString(array $headers, string $eol = "\r\n"): string - { - $out = ''; - foreach ($headers as $key => $value) { - if ($value === false || $value === null || $value === '') { - continue; - } - - foreach ((array)$value as $val) { - $out .= $key . ': ' . $val . $eol; - } - } - if (!empty($out)) { - $out = substr($out, 0, -1 * strlen($eol)); - } - - return $out; - } } diff --git a/src/Mailer/Message.php b/src/Mailer/Message.php index 8b0f513cc35..4d732c059eb 100644 --- a/src/Mailer/Message.php +++ b/src/Mailer/Message.php @@ -22,6 +22,7 @@ use Cake\Utility\Hash; use Cake\Utility\Security; use Cake\Utility\Text; +use Closure; use InvalidArgumentException; use JsonSerializable; use Serializable; @@ -854,8 +855,8 @@ public function addHeaders(array $headers) * - `bcc` * - `subject` * - * @param array $include List of headers. - * @return array + * @param string[] $include List of headers. + * @return string[] */ public function getHeaders(array $include = []): array { @@ -934,6 +935,40 @@ public function getHeaders(array $include = []): array return $headers; } + /** + * Get headers as string. + * + * @param string[] $include List of headers. + * @param \Closure $callback Callback to run each header value through before stringifying. + * @param string $eol End of line string. + * @return string + * @see Message::getHeaders() + */ + public function getHeadersString(array $include = [], ?Closure $callback = null, string $eol = "\r\n"): string + { + $headers = $this->getHeaders($include); + + if ($callback) { + $headers = array_map($callback, $headers); + } + + $out = ''; + foreach ($headers as $key => $value) { + if (empty($value) && $value !== '0') { + continue; + } + + foreach ((array)$value as $val) { + $out .= $key . ': ' . $val . $eol; + } + } + if (!empty($out)) { + $out = substr($out, 0, -1 * strlen($eol)); + } + + return $out; + } + /** * Format addresses * diff --git a/src/Mailer/Transport/DebugTransport.php b/src/Mailer/Transport/DebugTransport.php index fdd7632b6f2..b73ce77bda5 100644 --- a/src/Mailer/Transport/DebugTransport.php +++ b/src/Mailer/Transport/DebugTransport.php @@ -32,10 +32,9 @@ class DebugTransport extends AbstractTransport */ public function send(Message $message): array { - $headers = $message->getHeaders( + $headers = $message->getHeadersString( ['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject'] ); - $headers = $this->_headersToString($headers); $message = implode("\r\n", (array)$message->getBody()); return ['headers' => $headers, 'message' => $message]; diff --git a/src/Mailer/Transport/MailTransport.php b/src/Mailer/Transport/MailTransport.php index 8b7f65e1e18..0edf6d02109 100644 --- a/src/Mailer/Transport/MailTransport.php +++ b/src/Mailer/Transport/MailTransport.php @@ -38,22 +38,23 @@ public function send(Message $message): array if (isset($this->_config['eol'])) { $eol = $this->_config['eol']; } - $headers = $message->getHeaders([ - 'from', - 'sender', - 'replyTo', - 'readReceipt', - 'returnPath', - 'to', - 'cc', - 'bcc', - ]); - $to = $headers['To']; - unset($headers['To']); - foreach ($headers as $key => $header) { - $headers[$key] = str_replace(["\r", "\n"], '', $header); - } - $headers = $this->_headersToString($headers, $eol); + $to = $message->getHeaders(['to'])['To']; + $headers = $message->getHeadersString( + [ + 'from', + 'sender', + 'replyTo', + 'readReceipt', + 'returnPath', + 'cc', + 'bcc', + ], + function ($val) { + return str_replace(["\r", "\n"], '', $val); + }, + $eol + ); + $subject = str_replace(["\r", "\n"], '', $message->getSubject()); $to = str_replace(["\r", "\n"], '', $to); diff --git a/src/Mailer/Transport/SmtpTransport.php b/src/Mailer/Transport/SmtpTransport.php index f40a890cdb8..0b8c88824c3 100644 --- a/src/Mailer/Transport/SmtpTransport.php +++ b/src/Mailer/Transport/SmtpTransport.php @@ -408,7 +408,16 @@ protected function _sendData(Message $message): void { $this->_smtpSend('DATA', '354'); - $headers = $this->_headersToString($this->_prepareMessageHeaders($message)); + $headers = $message->getHeadersString([ + 'from', + 'sender', + 'replyTo', + 'readReceipt', + 'to', + 'cc', + 'subject', + 'returnPath' + ]); $message = $this->_prepareMessage($message); $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n."); diff --git a/tests/TestCase/Mailer/MessageTest.php b/tests/TestCase/Mailer/MessageTest.php index 301e86c8140..f5f7a0ee3cb 100644 --- a/tests/TestCase/Mailer/MessageTest.php +++ b/tests/TestCase/Mailer/MessageTest.php @@ -25,6 +25,18 @@ */ class MessageTest extends TestCase { + /** + * @var \Cake\Mailer\Message + */ + protected $message; + + public function setUp(): void + { + parent::setUp(); + + $this->message = new Message(); + } + /** * testWrap method * @@ -93,4 +105,105 @@ public function testWrap() ]; $this->assertSame($expected, $result); } + + /** + * testHeaders method + * + * @return void + */ + public function testHeaders() + { + $this->message->setMessageId(false); + $this->message->setHeaders(['X-Something' => 'nice']); + $expected = [ + 'X-Something' => 'nice', + 'Date' => date(DATE_RFC2822), + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/plain; charset=UTF-8', + 'Content-Transfer-Encoding' => '8bit', + ]; + $this->assertSame($expected, $this->message->getHeaders()); + + $this->message->addHeaders(['X-Something' => 'very nice', 'X-Other' => 'cool']); + $expected = [ + 'X-Something' => 'very nice', + 'X-Other' => 'cool', + 'Date' => date(DATE_RFC2822), + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/plain; charset=UTF-8', + 'Content-Transfer-Encoding' => '8bit', + ]; + $this->assertSame($expected, $this->message->getHeaders()); + + $this->message->setFrom('cake@cakephp.org'); + $this->assertSame($expected, $this->message->getHeaders()); + + $expected = [ + 'From' => 'cake@cakephp.org', + 'X-Something' => 'very nice', + 'X-Other' => 'cool', + 'Date' => date(DATE_RFC2822), + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/plain; charset=UTF-8', + 'Content-Transfer-Encoding' => '8bit', + ]; + $this->assertSame($expected, $this->message->getHeaders(['from' => true])); + + $this->message->setFrom('cake@cakephp.org', 'CakePHP'); + $expected['From'] = 'CakePHP '; + $this->assertSame($expected, $this->message->getHeaders(['from' => true])); + + $this->message->setTo(['cake@cakephp.org', 'php@cakephp.org' => 'CakePHP']); + $expected = [ + 'From' => 'CakePHP ', + 'To' => 'cake@cakephp.org, CakePHP ', + 'X-Something' => 'very nice', + 'X-Other' => 'cool', + 'Date' => date(DATE_RFC2822), + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/plain; charset=UTF-8', + 'Content-Transfer-Encoding' => '8bit', + ]; + $this->assertSame($expected, $this->message->getHeaders(['from' => true, 'to' => true])); + + $this->message->setCharset('ISO-2022-JP'); + $expected = [ + 'From' => 'CakePHP ', + 'To' => 'cake@cakephp.org, CakePHP ', + 'X-Something' => 'very nice', + 'X-Other' => 'cool', + 'Date' => date(DATE_RFC2822), + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/plain; charset=ISO-2022-JP', + 'Content-Transfer-Encoding' => '7bit', + ]; + $this->assertSame($expected, $this->message->getHeaders(['from' => true, 'to' => true])); + + $result = $this->message->setHeaders([]); + $this->assertInstanceOf(Message::class, $result); + + $this->message->setHeaders(['o:tag' => ['foo']]); + $this->message->addHeaders(['o:tag' => ['bar']]); + $result = $this->message->getHeaders(); + $this->assertEquals(['foo', 'bar'], $result['o:tag']); + } + + /** + * testHeadersString method + * + * @return void + */ + public function testHeadersString() + { + $this->message->setMessageId(false); + $this->message->setHeaders(['X-Something' => 'nice']); + $expected = [ + 'X-Something: nice', + 'Date: ' . date(DATE_RFC2822), + 'MIME-Version: 1.0', + 'Content-Type: text/plain; charset=UTF-8', + 'Content-Transfer-Encoding: 8bit', + ]; + $this->assertSame(implode("\r\n", $expected), $this->message->getHeadersString()); + } }