From 9ee9588c66cff93f50f12d5fa0ca817b2d4fd765 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 13:55:56 +0700 Subject: [PATCH 01/16] add supported phpunit versions --- composer.json | 2 +- phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 21579b8..db791d8 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || 9.3", "squizlabs/php_codesniffer": "^3.5" }, "suggest": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 66b98f0..6f90c5c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ - + tests From 9fc46e4df130ceeeab39e7ed6cc94fa2f23e36e7 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:00:32 +0700 Subject: [PATCH 02/16] added github actions CI configuration --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4e36ffc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + + strategy: + matrix: + php: [5.3, 5.4, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction --no-suggest + + - name: Check code style + run: vendor/bin/phpcs + + - name: Run test suite + run: vendor/bin/phpunit \ No newline at end of file From 71b0b50ca283b6cc72eb9e705c2573ba7335b144 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:05:20 +0700 Subject: [PATCH 03/16] moved code from setUp() method to constructor --- tests/MailerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MailerTest.php b/tests/MailerTest.php index 7bbe3a8..bcfded9 100644 --- a/tests/MailerTest.php +++ b/tests/MailerTest.php @@ -27,9 +27,9 @@ class MailerTest extends TestCase */ private $factory; - public function setUp() + public function __construct($name = null, $data = array(), $dataName = '') { - parent::setUp(); + parent::__construct($name, (array)$data, $dataName); $this->factory = new MessageFactory(); $this->transport = new MockTransport(function ($log) { }); From 3dad162b77e778cd36acbe8c7aa9216b11ced30a Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:07:30 +0700 Subject: [PATCH 04/16] added old phpcs versions for PHP 5.3 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index db791d8..2aad703 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || 9.3", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^2.9 || ^3.5" }, "suggest": { "ext-fileinfo": "Mime-Type detecting" From 3cf5c1a09bd4c1277d68f37cbfaa66f101b25fac Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:28:02 +0700 Subject: [PATCH 05/16] exclude code-style checks from PHP 5.3 --- .github/workflows/ci.yml | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e36ffc..fe9211b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,38 @@ name: CI on: [push, pull_request] jobs: + tests-php-5.3: + runs-on: ubuntu-latest + + strategy: + matrix: + php: 5.3 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction --no-suggest + + - name: Run test suite + run: vendor/bin/phpunit + tests: runs-on: ubuntu-latest strategy: matrix: - php: [5.3, 5.4, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] + php: [5.4, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] steps: - name: Checkout code From cc281422aa75c37ffc7b18ec7715d99626bca55b Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:28:54 +0700 Subject: [PATCH 06/16] fix config --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe9211b..4ad6903 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - php: 5.3 + php: [5.3] steps: - name: Checkout code From 2774ef96e9846f9533ff9078f473d65c83b709cd Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:30:07 +0700 Subject: [PATCH 07/16] fix config --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ad6903..b3c5e88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: [push, pull_request] jobs: - tests-php-5.3: + tests-php-5_3: runs-on: ubuntu-latest strategy: From e305c81a5b8cc24f3852cd94cb8902cf4a333d00 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 5 Nov 2020 14:36:01 +0700 Subject: [PATCH 08/16] upgrade version --- src/Mailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mailer.php b/src/Mailer.php index a18f3a9..53ed5b5 100644 --- a/src/Mailer.php +++ b/src/Mailer.php @@ -10,7 +10,7 @@ final class Mailer { - const MAILER_VERSION = "5.0.0"; + const MAILER_VERSION = "5.0.1"; /** * @var string|null From ed0ee3b9e5dfb5b9a242923018871c636b9a2ce0 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 29 Apr 2021 18:20:33 +0700 Subject: [PATCH 09/16] fix headers encoding --- src/Message.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Message.php b/src/Message.php index b270268..1f95995 100644 --- a/src/Message.php +++ b/src/Message.php @@ -959,10 +959,6 @@ private function encodeHeader($header, $value) if ($char === 32 && $num + 1 === $all) { $ascii = false; } - if ($num < $offset) { - $ascii = true; - $coding = false; - } if (!$coding && $char === 61 && preg_match('/;(\s+)?([a-z0-9\-]+)(\s+)?(=(\s+)?\"[^\"]+)?/ui', $result)) { $ascii = true; } From db6d1f2deb2013c27bba1cfe72f9489d621572ea Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 29 Apr 2021 18:21:21 +0700 Subject: [PATCH 10/16] upgrade version --- src/Mailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mailer.php b/src/Mailer.php index 53ed5b5..6fedc9d 100644 --- a/src/Mailer.php +++ b/src/Mailer.php @@ -10,7 +10,7 @@ final class Mailer { - const MAILER_VERSION = "5.0.1"; + const MAILER_VERSION = "5.0.2"; /** * @var string|null From e59fb2214d1dd287fc69e38e833754e3546d4145 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Thu, 29 Apr 2021 19:02:58 +0700 Subject: [PATCH 11/16] fix headers encoding --- src/Message.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Message.php b/src/Message.php index 1f95995..14081ad 100644 --- a/src/Message.php +++ b/src/Message.php @@ -962,6 +962,9 @@ private function encodeHeader($header, $value) if (!$coding && $char === 61 && preg_match('/;(\s+)?([a-z0-9\-]+)(\s+)?(=(\s+)?\"[^\"]+)?/ui', $result)) { $ascii = true; } + if ($coding && $symbol === ' ') { + $ascii = false; + } if ($ascii) { if ($coding) { $coding = false; @@ -981,8 +984,13 @@ private function encodeHeader($header, $value) $add += 3; } if ($position + $add >= $max) { - $line = "=\r\n $line"; - $position = $add + 1; + if ($coding) { + $line = "?=\r\n =?utf-8?Q?$line"; + $position = $add + 11; + } else { + $line = "=\r\n $line"; + $position = $add + 1; + } } $result .= $line; $position += $add; From 428122a392b2fdd53bef41c397ed99ef735a7258 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Sat, 26 Mar 2022 13:14:38 +0300 Subject: [PATCH 12/16] fix code style --- src/Contract/Message.php | 2 -- src/Contract/Spool.php | 1 - src/Contract/Transport.php | 1 - src/Exception/HeaderNotModifiedException.php | 1 - src/Exception/InvalidAttachmentNameException.php | 1 - src/Exception/InvalidEmailException.php | 1 - src/Exception/RecipientsListEmptyException.php | 1 - src/Exception/TransportException.php | 1 - src/Mailer.php | 1 - src/Message.php | 1 - src/Spool/FileSpool.php | 1 - src/Spool/MemorySpool.php | 1 - src/Transport/FakeTransport.php | 1 - src/Transport/FileTransport.php | 1 - src/Transport/SendmailTransport.php | 1 - src/Transport/SmtpTransport.php | 1 - src/Transport/SpoolTransport.php | 1 - src/TransportFactory.php | 1 - stuff/Factory/MessageFactory.php | 1 - tests/MailerTest.php | 1 - 20 files changed, 21 deletions(-) diff --git a/src/Contract/Message.php b/src/Contract/Message.php index 45f956b..1e7aef8 100644 --- a/src/Contract/Message.php +++ b/src/Contract/Message.php @@ -6,8 +6,6 @@ interface Message extends Serializable { - - /** * @param string $email Sender email. * @param string|null $name Sender name. diff --git a/src/Contract/Spool.php b/src/Contract/Spool.php index e4cb583..a380dbe 100644 --- a/src/Contract/Spool.php +++ b/src/Contract/Spool.php @@ -4,7 +4,6 @@ interface Spool { - /** * @param Message $message * @param int $attempt diff --git a/src/Contract/Transport.php b/src/Contract/Transport.php index d441bbe..4fcf759 100644 --- a/src/Contract/Transport.php +++ b/src/Contract/Transport.php @@ -6,7 +6,6 @@ interface Transport { - /** * @param Message $message * @return bool diff --git a/src/Exception/HeaderNotModifiedException.php b/src/Exception/HeaderNotModifiedException.php index cb7421b..9d00c76 100644 --- a/src/Exception/HeaderNotModifiedException.php +++ b/src/Exception/HeaderNotModifiedException.php @@ -6,7 +6,6 @@ final class HeaderNotModifiedException extends InvalidArgumentException { - public function __construct($header, $method = null) { $message = 'header "' . $header . '" is not modified.'; diff --git a/src/Exception/InvalidAttachmentNameException.php b/src/Exception/InvalidAttachmentNameException.php index 9d1f9c4..2c8ab21 100644 --- a/src/Exception/InvalidAttachmentNameException.php +++ b/src/Exception/InvalidAttachmentNameException.php @@ -6,7 +6,6 @@ final class InvalidAttachmentNameException extends InvalidArgumentException { - public function __construct($name) { parent::__construct('invalid attachment ' . $name, 1); diff --git a/src/Exception/InvalidEmailException.php b/src/Exception/InvalidEmailException.php index 19efdd3..6f3aa27 100644 --- a/src/Exception/InvalidEmailException.php +++ b/src/Exception/InvalidEmailException.php @@ -6,7 +6,6 @@ final class InvalidEmailException extends InvalidArgumentException { - public function __construct($email) { parent::__construct('email ' . $email . ' is invalid', 1); diff --git a/src/Exception/RecipientsListEmptyException.php b/src/Exception/RecipientsListEmptyException.php index 18bfa1c..9423656 100644 --- a/src/Exception/RecipientsListEmptyException.php +++ b/src/Exception/RecipientsListEmptyException.php @@ -6,7 +6,6 @@ final class RecipientsListEmptyException extends Exception { - public function __construct() { parent::__construct('recipients list is empty', 1); diff --git a/src/Exception/TransportException.php b/src/Exception/TransportException.php index 5027f71..2c48d4c 100644 --- a/src/Exception/TransportException.php +++ b/src/Exception/TransportException.php @@ -6,7 +6,6 @@ final class TransportException extends RuntimeException { - public function __construct($message, $code) { parent::__construct($message, $code); diff --git a/src/Mailer.php b/src/Mailer.php index 6fedc9d..c4d3c76 100644 --- a/src/Mailer.php +++ b/src/Mailer.php @@ -9,7 +9,6 @@ final class Mailer { - const MAILER_VERSION = "5.0.2"; /** diff --git a/src/Message.php b/src/Message.php index 14081ad..e55534c 100644 --- a/src/Message.php +++ b/src/Message.php @@ -10,7 +10,6 @@ final class Message implements MessageContract { - const RECIPIENT_TO = 'to'; const RECIPIENT_CC = 'cc'; const RECIPIENT_BCC = 'bcc'; diff --git a/src/Spool/FileSpool.php b/src/Spool/FileSpool.php index 27a6573..5f04ad9 100644 --- a/src/Spool/FileSpool.php +++ b/src/Spool/FileSpool.php @@ -7,7 +7,6 @@ final class FileSpool implements Spool { - /** * @var string */ diff --git a/src/Spool/MemorySpool.php b/src/Spool/MemorySpool.php index 35df94f..08fe1aa 100644 --- a/src/Spool/MemorySpool.php +++ b/src/Spool/MemorySpool.php @@ -7,7 +7,6 @@ final class MemorySpool implements Spool { - /** * @var Message[][] */ diff --git a/src/Transport/FakeTransport.php b/src/Transport/FakeTransport.php index 49d875b..7ab6674 100644 --- a/src/Transport/FakeTransport.php +++ b/src/Transport/FakeTransport.php @@ -7,7 +7,6 @@ final class FakeTransport implements Transport { - /** * @inheritDoc */ diff --git a/src/Transport/FileTransport.php b/src/Transport/FileTransport.php index f665dc5..4f5eeaf 100644 --- a/src/Transport/FileTransport.php +++ b/src/Transport/FileTransport.php @@ -7,7 +7,6 @@ final class FileTransport implements Transport { - /** * @var string */ diff --git a/src/Transport/SendmailTransport.php b/src/Transport/SendmailTransport.php index 629a8f1..3e49724 100644 --- a/src/Transport/SendmailTransport.php +++ b/src/Transport/SendmailTransport.php @@ -7,7 +7,6 @@ final class SendmailTransport implements Transport { - /** * @var string */ diff --git a/src/Transport/SmtpTransport.php b/src/Transport/SmtpTransport.php index e6a03aa..f11e07a 100644 --- a/src/Transport/SmtpTransport.php +++ b/src/Transport/SmtpTransport.php @@ -9,7 +9,6 @@ final class SmtpTransport implements Transport { - const ENCRYPTION_TLS = 'tls'; const ENCRYPTION_SSL = 'ssl'; diff --git a/src/Transport/SpoolTransport.php b/src/Transport/SpoolTransport.php index eff83db..8b77988 100644 --- a/src/Transport/SpoolTransport.php +++ b/src/Transport/SpoolTransport.php @@ -9,7 +9,6 @@ final class SpoolTransport implements Transport { - /** * @var Transport */ diff --git a/src/TransportFactory.php b/src/TransportFactory.php index 8009c5e..5ab34b4 100644 --- a/src/TransportFactory.php +++ b/src/TransportFactory.php @@ -11,7 +11,6 @@ final class TransportFactory { - /** * @param string $transportUrl * @return Transport diff --git a/stuff/Factory/MessageFactory.php b/stuff/Factory/MessageFactory.php index cd9bf64..dab0240 100644 --- a/stuff/Factory/MessageFactory.php +++ b/stuff/Factory/MessageFactory.php @@ -6,7 +6,6 @@ final class MessageFactory { - private $from; public function __construct() diff --git a/tests/MailerTest.php b/tests/MailerTest.php index bcfded9..60793fb 100644 --- a/tests/MailerTest.php +++ b/tests/MailerTest.php @@ -11,7 +11,6 @@ class MailerTest extends TestCase { - /** * @var Mailer */ From 16ae197ce100eccd5be6efcd9c8dbf087c8fc383 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Sat, 26 Mar 2022 13:36:07 +0300 Subject: [PATCH 13/16] create method for body encoding and small cosmetics fixes --- src/Contract/Message.php | 4 +- src/Message.php | 88 +++++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/Contract/Message.php b/src/Contract/Message.php index 1e7aef8..a0987d2 100644 --- a/src/Contract/Message.php +++ b/src/Contract/Message.php @@ -9,7 +9,7 @@ interface Message extends Serializable /** * @param string $email Sender email. * @param string|null $name Sender name. - * @return self + * @return $this */ public function setSender($email, $name = null); @@ -19,7 +19,7 @@ public function setSender($email, $name = null); public function getRecipients(); /** - * @return string Rew string as email headers + * @return string Raw string as email headers */ public function getHeadersRaw(); diff --git a/src/Message.php b/src/Message.php index e55534c..6e46d8a 100644 --- a/src/Message.php +++ b/src/Message.php @@ -138,7 +138,7 @@ final class Message implements MessageContract 'eps' => 'application/postscript', 'ps' => 'application/postscript', - // ms office + // MS Office 'doc' => 'application/msword', 'rtf' => 'application/rtf', 'xls' => 'application/vnd.ms-excel', @@ -187,7 +187,7 @@ public function __clone() /** * @param string|null $subject Subject of message. - * @return self + * @return $this */ public function setSubject($subject) { @@ -198,7 +198,7 @@ public function setSubject($subject) /** * @param string|null $html HTML text of message. - * @return self + * @return $this */ public function setHtml($html = null) { @@ -216,7 +216,7 @@ public function setHtml($html = null) /** * @param string|null $text Plain text of message. - * @return self + * @return $this */ public function setText($text = null) { @@ -258,7 +258,7 @@ public function setSender($email, $name = null) * @param string $email Recipient email. * @param string|null $name Recipient name. * @param string $type Recipient type. May be 'to', 'cc' or 'bcc'. Default 'to'. - * @return self + * @return $this * @throws InvalidEmailException */ public function addRecipient($email, $name = null, $type = self::RECIPIENT_TO) @@ -290,7 +290,7 @@ public function getRecipientName($email) /** * @param string $email Recipient email. - * @return self + * @return $this */ public function removeRecipient($email) { @@ -303,7 +303,7 @@ public function removeRecipient($email) /** * @param string $type Recipient type. May be 'to', 'cc', 'bcc' or null. Default null. - * @return self + * @return $this */ public function removeRecipients($type = null) { @@ -327,7 +327,7 @@ public function removeRecipients($type = null) * @param string $name * @param string $content * @param string|null $mime - * @return self + * @return $this */ public function attachFromString($name, $content, $mime = null) { @@ -348,7 +348,7 @@ public function attachFromString($name, $content, $mime = null) * @param string $name * @param string $path * @param string|null $mime - * @return self + * @return $this */ public function attachFromFile($name, $path, $mime = null) { @@ -368,7 +368,7 @@ public function attachFromFile($name, $path, $mime = null) /** * @param string $name - * @return self + * @return $this */ public function detach($name) { @@ -384,7 +384,7 @@ public function detach($name) * @param string $id * @param string $content * @param string $mime - * @return self + * @return $this */ public function setHtmlContentFromString($id, $content, $mime = 'application/octet-stream') { @@ -405,7 +405,7 @@ public function setHtmlContentFromString($id, $content, $mime = 'application/oct * @param string $id * @param string $path * @param string $mime - * @return self + * @return $this */ public function setHtmlContentFromFile($id, $path, $mime = 'application/octet-stream') { @@ -425,7 +425,7 @@ public function setHtmlContentFromFile($id, $path, $mime = 'application/octet-st /** * @param string $id - * @return self + * @return $this */ public function unsetBodyHtmlContent($id) { @@ -440,7 +440,7 @@ public function unsetBodyHtmlContent($id) /** * @param string $header Header name. * @param string|null $value Header values. - * @return self + * @return $this */ public function setHeader($header, $value) { @@ -475,7 +475,7 @@ public function getHeader($header) /** * @param string $header Header name. - * @return string|null Header values. + * @return $this */ public function removeHeader($header) { @@ -485,7 +485,7 @@ public function removeHeader($header) } /** - * @return string[] Recipients emails. + * @inheritDoc */ public function getRecipients() { @@ -501,7 +501,7 @@ public function getSubject() } /** - * @return string Rew string as email headers + * @inheritDoc */ public function getHeadersRaw() { @@ -540,6 +540,9 @@ public function getBodyRaw() return $info['data']; } + /** + * @inheritDoc + */ public function getPersonalMessages() { $messages = array(); @@ -572,9 +575,9 @@ public function serialize() /** * @inheritDoc */ - public function unserialize($serialized) + public function unserialize($data) { - $raw = unserialize($serialized); + $raw = unserialize($data); $empty = array( 'id' => array(), 'headers' => array(), @@ -650,7 +653,7 @@ private function getMainInfo($onlyType) if (!is_null($this->text) && is_null($this->html)) { $result['type'] = 'text/plain; charset=UTF-8'; if (!$onlyType) { - $result['data'] = quoted_printable_encode($this->text); + $result['data'] = $this->encodeBody($this->text); } return $result; } @@ -671,7 +674,7 @@ private function getMainInfo($onlyType) $text .= 'Content-Type: text/plain; charset=UTF-8' . $eol; $text .= 'Content-Transfer-Encoding: quoted-printable' . $eol; $text .= $eol; - $text .= quoted_printable_encode($this->text) . $eol; + $text .= $this->encodeBody($this->text) . $eol; $html = $eol; $html .= $this->encodeHeader('Content-Type', $htmlInfo['type']) . $eol; @@ -698,7 +701,7 @@ private function getHtmlInfo($onlyType) if (is_null($this->html)) { return $result; } - $raw = quoted_printable_encode($this->html); + $raw = $this->encodeBody($this->html); if (empty($this->contents)) { $result['type'] = 'text/html; charset=UTF-8'; } else { @@ -749,16 +752,13 @@ private function randomString() { $start = 268435456; $finish = 4294967295; - $rand = null; - if (function_exists('random_int')) { - try { - /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ - $rand = random_int($start, $finish); - } catch (Exception $e) { - $rand = null; - } + if (!function_exists('random_int')) { + return dechex(rand($start, $finish)); } - if (!$rand) { + + try { + $rand = random_int($start, $finish); + } catch (Exception $e) { $rand = rand($start, $finish); } return dechex($rand); @@ -767,14 +767,14 @@ private function randomString() /** * @param string $header Header name. * @param string $value Header values. - * @return self + * @return void */ private function setAnyHeader($header, $value) { $header = $this->prepareHeaderName($header); $value = $this->prepareHeaderValue($value); if (!$header) { - return $this; + return; } if ($value) { $this->headers[$header] = $value; @@ -783,12 +783,11 @@ private function setAnyHeader($header, $value) unset($this->headers[$header]); } } - return $this; } /** * @param string $header Header name. - * @return string|null Header values. + * @return void */ private function removeAnyHeader($header) { @@ -796,13 +795,12 @@ private function removeAnyHeader($header) if (array_key_exists($header, $this->headers)) { unset($this->headers[$header]); } - return $this; } /** * @param string $header * @param bool $removing - * @return bool + * @return void * @throws HeaderNotModifiedException */ private function touchHeader($header, $removing) @@ -814,7 +812,6 @@ private function touchHeader($header, $removing) $method = is_array($this->protectedHeaders[$header]) ? $this->protectedHeaders[$header][$key] : null; throw new HeaderNotModifiedException($header, $method); } - return true; } /** @@ -829,14 +826,13 @@ private function normalizeHeaderName($name) if ($name === 'message-id') { return 'Message-ID'; } - $name = preg_replace_callback( + return preg_replace_callback( '/(^|-)[a-z]/ui', function ($match) { return strtoupper($match[0]); }, $name ); - return $name; } /** @@ -931,8 +927,7 @@ private function prepareAttachmentName($name) */ private function prepareContentId($name) { - $name = (string)$name; - return $name; + return (string)$name; } /** @@ -1004,6 +999,15 @@ private function encodeHeader($header, $value) return $result; } + /** + * @param string $data + * @return string + */ + private function encodeBody($data) + { + return quoted_printable_encode($data); + } + /** * @param string $pathOrContent Path to file or Contents * @return string From 44ce633f3c9e176440a31e056c6bc7e2b7141fd8 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Sat, 26 Mar 2022 20:29:02 +0300 Subject: [PATCH 14/16] fix headers encoding and fix removed dot on smtp --- src/Message.php | 132 ++++++++++++++++++++++++-------- src/Transport/SmtpTransport.php | 6 +- 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/Message.php b/src/Message.php index 6e46d8a..6a9f1da 100644 --- a/src/Message.php +++ b/src/Message.php @@ -939,44 +939,43 @@ private function encodeHeader($header, $value) { $max = 74; $offset = strlen($header) + 2; - $symbols = str_split($value); + $letters = mb_str_split($value); + $hasOptions = preg_match('/;(\s+)?([a-z0-9\-]+)(\s+)?(=(\s+)?\"[^\"]+)?/ui', $value); unset($value); $result = $header . ': '; $coding = false; - $all = count($symbols); + $all = count($letters); $position = $offset; - foreach ($symbols as $num => $symbol) { + foreach ($letters as $num => $letter) { $line = ''; - $add = 0; - $char = ord($symbol); - $ascii = ($char >= 32 && $char <= 60) || ($char >= 62 && $char <= 126); - if ($char === 32 && $num + 1 === $all) { - $ascii = false; - } - if (!$coding && $char === 61 && preg_match('/;(\s+)?([a-z0-9\-]+)(\s+)?(=(\s+)?\"[^\"]+)?/ui', $result)) { - $ascii = true; - } - if ($coding && $symbol === ' ') { - $ascii = false; - } - if ($ascii) { - if ($coding) { - $coding = false; - $line = '?=' . $symbol; - $add = 3; - } else { - $line = $symbol; - $add = 1; - } + /** + * @var string $char + * @var bool $encoded + */ + + if (!$coding && $letter === '=' && $hasOptions) { + $char = '='; + $encoded = false; } else { + list($char, $encoded) = $this->encodeLetter($letter, $num, $all, $coding); + } + + if ($encoded) { if (!$coding) { $coding = true; $line = '=?utf-8?Q?'; - $add = 10; } - $line .= $this->map[$char]; - $add += 3; + $line .= $char; + } else { + if ($coding) { + $coding = false; + $line = '?=' . $char; + } else { + $line = $char; + } } + $add = strlen($line); + if ($position + $add >= $max) { if ($coding) { $line = "?=\r\n =?utf-8?Q?$line"; @@ -996,16 +995,87 @@ private function encodeHeader($header, $value) } $result .= $line; } - return $result; + return str_replace( + array("\t\r\n", " \r\n"), + array("=09\r\n", "=20\r\n"), + $result + ); + } + + /** + * @param string $letter + * @param int $position + * @param int $length + * @return array + */ + private function encodeLetter($letter, $position, $length, $coding) + { + $result = ''; + if ($letter === ' ' && $coding) { + return array($this->encodeSymbol($letter, true), true); + } + $isNeedEncode = (strlen($letter) > 1); + $symbols = str_split($letter); + foreach ($symbols as $symbol) { + if ($this->isNeedEncode($symbol, $position, $length)) { + $isNeedEncode = true; + } + } + foreach ($symbols as $symbol) { + $result .= $this->encodeSymbol($symbol, $isNeedEncode); + } + return array($result, $isNeedEncode); + } + + /** + * @param string$symbol + * @param int $position + * @param int $length + * @return bool + */ + private function isNeedEncode($symbol, $position, $length) + { + $char = ord($symbol); + $isNeedEncode = !($char === 9 || ($char >= 32 && $char <= 60) || ($char >= 62 && $char <= 126)); + if ((in_array($char, array(9, 20, 32)) && $position + 1 === $length)) { + $isNeedEncode = true; + } + return $isNeedEncode; } /** - * @param string $data + * @param string $symbol + * @param bool $isNeedEncode * @return string */ - private function encodeBody($data) + private function encodeSymbol($symbol, $isNeedEncode) { - return quoted_printable_encode($data); + $char = ord($symbol); + + if ($isNeedEncode) { + return $this->map[$char]; + } + return $symbol; + } + + /** + * @param string $string + * @return string + */ + private function encodeBody($string) + { + $string = quoted_printable_encode($string); + $string = str_replace( + array("\t\r\n", " \r\n"), + array("=09\r\n", "=20\r\n"), + $string + ); + $last = ord(substr($string, -1)); + if (in_array($last, array(9, 20))) { + $string = substr_replace($string, sprintf('=%\'.02d', $last), -1); + } + + return $string; } /** diff --git a/src/Transport/SmtpTransport.php b/src/Transport/SmtpTransport.php index f11e07a..b34864f 100644 --- a/src/Transport/SmtpTransport.php +++ b/src/Transport/SmtpTransport.php @@ -174,7 +174,11 @@ public function send(Message $message) $this->smtpCommand('RCPT TO: <' . $address . '>'); } $this->smtpCommand('DATA'); - $data = $message->getHeadersRaw() . "\r\n\r\n" . $message->getBodyRaw() . "\r\n.\r\n"; + $data = $message->getHeadersRaw() + . "\r\n\r\n" + . str_replace("\r\n.", "\r\n..", $message->getBodyRaw()) + . "\r\n.\r\n" + ; $this->smtpCommand($data); return true; } From a257043af60e8a350598d202ac3a5b24cd6a3cbc Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Sat, 26 Mar 2022 20:30:23 +0300 Subject: [PATCH 15/16] upgrade version --- src/Mailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mailer.php b/src/Mailer.php index c4d3c76..4de2356 100644 --- a/src/Mailer.php +++ b/src/Mailer.php @@ -9,7 +9,7 @@ final class Mailer { - const MAILER_VERSION = "5.0.2"; + const MAILER_VERSION = "5.0.4"; /** * @var string|null From 3f5a0edfbaca1eb7406d8507458a8d8388ddd711 Mon Sep 17 00:00:00 2001 From: Ivan Dudarev Date: Sat, 26 Mar 2022 20:47:56 +0300 Subject: [PATCH 16/16] supports old PHP versions --- composer.json | 3 ++- fn/fn.php | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 fn/fn.php diff --git a/composer.json b/composer.json index 2aad703..e841973 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "autoload": { "psr-4": { "Ddrv\\Mailer\\": "src/" - } + }, + "files": ["fn/fn.php"] }, "autoload-dev": { "psr-4": { diff --git a/fn/fn.php b/fn/fn.php new file mode 100644 index 0000000..63bb1fa --- /dev/null +++ b/fn/fn.php @@ -0,0 +1,62 @@ + $split_length) { + trigger_error('mb_str_split(): The length of each segment must be greater than zero', E_USER_WARNING); + return false; + } + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } else { + $encoding = (string) $encoding; + } + + if (!in_array($encoding, mb_list_encodings(), true)) { + static $aliases; + if ($aliases === null) { + $aliases = array(); + foreach (mb_list_encodings() as $encoding) { + $encoding_aliases = mb_encoding_aliases($encoding); + if ($encoding_aliases) { + foreach ($encoding_aliases as $alias) { + $aliases[] = $alias; + } + } + } + } + if (! in_array($encoding, $aliases, true)) { + trigger_error('mb_str_split(): Unknown encoding "' . $encoding . '"', E_USER_WARNING); + return null; + } + } + + $result = array(); + $length = mb_strlen($string, $encoding); + for ($i = 0; $i < $length; $i += $split_length) { + $result[] = mb_substr($string, $i, $split_length, $encoding); + } + return $result; + } +}