Browse Source

Add method to Message for getting headers as string.

ADmad 6 years ago
parent
commit
ce8c7e80e3

+ 4 - 27
src/Mailer/AbstractTransport.php

@@ -64,33 +64,10 @@ abstract class AbstractTransport
             && $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;
-    }
 }

+ 37 - 2
src/Mailer/Message.php

@@ -22,6 +22,7 @@ use Cake\Http\Client\FormDataPart;
 use Cake\Utility\Hash;
 use Cake\Utility\Security;
 use Cake\Utility\Text;
+use Closure;
 use InvalidArgumentException;
 use JsonSerializable;
 use Serializable;
@@ -854,8 +855,8 @@ class Message implements JsonSerializable, Serializable
      * - `bcc`
      * - `subject`
      *
-     * @param array $include List of headers.
-     * @return array
+     * @param string[] $include List of headers.
+     * @return string[]
      */
     public function getHeaders(array $include = []): array
     {
@@ -935,6 +936,40 @@ class Message implements JsonSerializable, Serializable
     }
 
     /**
+     * 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
      *
      * If the address contains non alphanumeric/whitespace characters, it will

+ 1 - 2
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];

+ 17 - 16
src/Mailer/Transport/MailTransport.php

@@ -38,22 +38,23 @@ class MailTransport extends AbstractTransport
         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);
 

+ 10 - 1
src/Mailer/Transport/SmtpTransport.php

@@ -408,7 +408,16 @@ class SmtpTransport extends AbstractTransport
     {
         $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.");

+ 113 - 0
tests/TestCase/Mailer/MessageTest.php

@@ -26,6 +26,18 @@ use TestApp\Mailer\TestMessage;
 class MessageTest extends TestCase
 {
     /**
+     * @var \Cake\Mailer\Message
+     */
+    protected $message;
+
+    public function setUp(): void
+    {
+        parent::setUp();
+
+        $this->message = new Message();
+    }
+
+    /**
      * testWrap method
      *
      * @return void
@@ -93,4 +105,105 @@ class MessageTest extends TestCase
         ];
         $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 <cake@cakephp.org>';
+        $this->assertSame($expected, $this->message->getHeaders(['from' => true]));
+
+        $this->message->setTo(['cake@cakephp.org', 'php@cakephp.org' => 'CakePHP']);
+        $expected = [
+            'From' => 'CakePHP <cake@cakephp.org>',
+            'To' => 'cake@cakephp.org, CakePHP <php@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, 'to' => true]));
+
+        $this->message->setCharset('ISO-2022-JP');
+        $expected = [
+            'From' => 'CakePHP <cake@cakephp.org>',
+            'To' => 'cake@cakephp.org, CakePHP <php@cakephp.org>',
+            '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());
+    }
 }