├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── UPGRADE.md ├── composer.json ├── config ├── common.php └── params.php └── src ├── Mailer.php └── Message.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Yii Mailer Library - Swift Mailer Extension Change Log 2 | 3 | 4 | ## 3.0.2 under development 5 | 6 | - no changes in this release. 7 | 8 | 9 | ## 3.0.1 August 30, 2021 10 | 11 | - Chg #45: Use definitions from `yiisoft/definitions` in configuration (vjik) 12 | 13 | ## 3.0.0 August 25, 2021 14 | 15 | - New #44: Add methods to `Yiisoft\Mailer\SwiftMailer\Message`: `getDate()`, `withDate()`, `getSender()`, `withSender()` (devanych) 16 | - Bug #44: Add normalization of return value to `Yiisoft\Mailer\SwiftMailer\Message` getters: `getFrom()`, `getTo()`, `getCc()`, `getBcc()` (devanych) 17 | 18 | ## 2.0.0 August 24, 2021 19 | 20 | - Chg: Use yiisoft/mailer ^2.0 (samdark) 21 | 22 | ## 1.0.0 July 05, 2021 23 | 24 | Initial release. 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2008 by Yii Software (https://www.yiiframework.com/) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Yii Software nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | This package is deprecated in favor of 4 | Symfony Mailer Extension. 5 | 6 |

7 |

8 | See details in article 9 | The end of Swiftmailer 10 | by Fabien Potencier. 11 |

12 |

13 | ❌ 14 |

15 | 16 | --- 17 | 18 |

19 | 20 | 21 | 22 | 23 | 24 | 25 |

Yii Mailer Library - Swift Mailer Extension

26 |
27 |

28 | 29 | [![Latest Stable Version](https://poser.pugx.org/yiisoft/mailer-swiftmailer/v/stable.png)](https://packagist.org/packages/yiisoft/mailer-swiftmailer) 30 | [![Total Downloads](https://poser.pugx.org/yiisoft/mailer-swiftmailer/downloads.png)](https://packagist.org/packages/yiisoft/mailer-swiftmailer) 31 | [![Build status](https://github.com/yiisoft/mailer-swiftmailer/workflows/build/badge.svg)](https://github.com/yiisoft/mailer-swiftmailer/actions?query=workflow%3Abuild) 32 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/mailer-swiftmailer/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/mailer-swiftmailer/?branch=master) 33 | [![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/mailer-swiftmailer/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/mailer-swiftmailer/?branch=master) 34 | [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Fmailer-swiftmailer%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/mailer-swiftmailer/master) 35 | [![static analysis](https://github.com/yiisoft/mailer-swiftmailer/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/mailer-swiftmailer/actions?query=workflow%3A%22static+analysis%22) 36 | [![type-coverage](https://shepherd.dev/github/yiisoft/mailer-swiftmailer/coverage.svg)](https://shepherd.dev/github/yiisoft/mailer-swiftmailer) 37 | 38 | This package is a [yiisoft/mailer](https://github.com/yiisoft/mailer) library implementation that provides 39 | a [Swift Mailer](https://swiftmailer.symfony.com/) mail solution. 40 | 41 | ## Installation 42 | 43 | The package could be installed with composer: 44 | 45 | ``` 46 | composer require yiisoft/mailer-swiftmailer --prefer-dist 47 | ``` 48 | 49 | ## General usage 50 | 51 | Creating a mailer: 52 | 53 | ```php 54 | use Yiisoft\Mailer\MessageBodyRenderer; 55 | use Yiisoft\Mailer\MessageBodyTemplate; 56 | use Yiisoft\Mailer\MessageFactory; 57 | use Yiisoft\Mailer\SwiftMailer\Mailer; 58 | use Yiisoft\Mailer\SwiftMailer\Message; 59 | 60 | /** 61 | * @var \Psr\EventDispatcher\EventDispatcherInterface $dispatcher 62 | * @var \Swift_Events_EventListener[] $plugins 63 | * @var \Swift_Transport $transport 64 | * @var \Yiisoft\View\View $view 65 | */ 66 | 67 | $template = new MessageBodyTemplate('/path/to/directory/of/view-files'); 68 | 69 | $mailer = new Mailer( 70 | new MessageFactory(Message::class), 71 | new MessageBodyRenderer($view, $template), 72 | $dispatcher, 73 | $transport, 74 | $plugins, // By default, an empty array 75 | ); 76 | ``` 77 | 78 | Sending a mail message: 79 | 80 | ```php 81 | $message = $mailer 82 | ->compose() 83 | ->withFrom('from@domain.com') 84 | ->withTo('to@domain.com') 85 | ->withSubject('Message subject') 86 | ->withTextBody('Plain text content') 87 | ->withHtmlBody('HTML content') 88 | ; 89 | $mailer->send($message); 90 | // Or several 91 | $mailer->sendMultiple([$message]); 92 | ``` 93 | 94 | Additional methods of the `Yiisoft\Mailer\SwiftMailer\Message`: 95 | 96 | - `getSwiftMessage()` - Returns a Swift message instance. 97 | - `getReadReceiptTo()` - Returns the addresses to which a read-receipt will be sent. 98 | - `withReadReceiptTo()` - Returns a new instance with the specified ask for a delivery receipt from the recipient to be sent to address. 99 | - `withAttachedSigners()` - Returns a new instance with the specified attached signers. 100 | 101 | For use in the [Yii framework](http://www.yiiframework.com/), see the configuration files: 102 | 103 | - [`config/common.php`](https://github.com/yiisoft/mailer-swiftmailer/blob/master/config/common.php) 104 | - [`config/params.php`](https://github.com/yiisoft/mailer-swiftmailer/blob/master/config/params.php) 105 | 106 | See [Yii guide to mailing](https://github.com/yiisoft/docs/blob/master/guide/en/tutorial/mailing.md) for more info. 107 | 108 | ## Testing 109 | 110 | ### Unit testing 111 | 112 | The package is tested with [PHPUnit](https://phpunit.de/). To run tests: 113 | 114 | ```shell 115 | ./vendor/bin/phpunit 116 | ``` 117 | 118 | ### Mutation testing 119 | 120 | The package tests are checked with [Infection](https://infection.github.io/) mutation framework with 121 | [Infection Static Analysis Plugin](https://github.com/Roave/infection-static-analysis-plugin). To run it: 122 | 123 | ```shell 124 | ./vendor/bin/roave-infection-static-analysis-plugin 125 | ``` 126 | 127 | ### Static analysis 128 | 129 | The code is statically analyzed with [Psalm](https://psalm.dev/). To run static analysis: 130 | 131 | ```shell 132 | ./vendor/bin/psalm 133 | ``` 134 | 135 | ## License 136 | 137 | The Yii Framework Swift Mailer Extension is free software. It is released under the terms of the BSD License. 138 | Please see [`LICENSE`](./LICENSE.md) for more information. 139 | 140 | Maintained by [Yii Software](https://www.yiiframework.com/). 141 | 142 | ## Support the project 143 | 144 | [![Open Collective](https://img.shields.io/badge/Open%20Collective-sponsor-7eadf1?logo=open%20collective&logoColor=7eadf1&labelColor=555555)](https://opencollective.com/yiisoft) 145 | 146 | ## Follow updates 147 | 148 | [![Official website](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=flat)](https://www.yiiframework.com/) 149 | [![Twitter](https://img.shields.io/badge/twitter-follow-1DA1F2?logo=twitter&logoColor=1DA1F2&labelColor=555555?style=flat)](https://twitter.com/yiiframework) 150 | [![Telegram](https://img.shields.io/badge/telegram-join-1DA1F2?style=flat&logo=telegram)](https://t.me/yii3en) 151 | [![Facebook](https://img.shields.io/badge/facebook-join-1DA1F2?style=flat&logo=facebook&logoColor=ffffff)](https://www.facebook.com/groups/yiitalk) 152 | [![Slack](https://img.shields.io/badge/slack-join-1DA1F2?style=flat&logo=slack)](https://yiiframework.com/go/slack) 153 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | Upgrading Instructions 2 | ====================== 3 | 4 | Changes summary: 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiisoft/mailer-swiftmailer", 3 | "description": "Yii Mailer Library - Swift Mailer Extension", 4 | "keywords": [ 5 | "yii", 6 | "swift", 7 | "swiftmailer", 8 | "mail", 9 | "email", 10 | "mailer" 11 | ], 12 | "type": "library", 13 | "license": "BSD-3-Clause", 14 | "support": { 15 | "issues": "https://github.com/yiisoft/mailer-swiftmailer/issues?state=open", 16 | "forum": "https://www.yiiframework.com/forum/", 17 | "wiki": "https://www.yiiframework.com/wiki/", 18 | "irc": "irc://irc.freenode.net/yii", 19 | "chat": "https://t.me/yii3en", 20 | "source": "https://github.com/yiisoft/mailer-swiftmailer" 21 | }, 22 | "funding": [ 23 | { 24 | "type": "opencollective", 25 | "url": "https://opencollective.com/yiisoft" 26 | }, 27 | { 28 | "type": "github", 29 | "url": "https://github.com/sponsors/yiisoft" 30 | } 31 | ], 32 | "require": { 33 | "php": "^7.4|^8.0", 34 | "swiftmailer/swiftmailer": "^6.2", 35 | "yiisoft/mailer": "^3.0" 36 | }, 37 | "require-dev": { 38 | "phpunit/phpunit": "^9.5", 39 | "roave/infection-static-analysis-plugin": "^1.16", 40 | "spatie/phpunit-watcher": "^1.23", 41 | "vimeo/psalm": "^4.18", 42 | "yiisoft/files": "^1.0", 43 | "yiisoft/psr-dummy-provider": "^1.0", 44 | "yiisoft/test-support": "^1.3" 45 | }, 46 | "suggest": { 47 | "ext-openssl": "Required for SMimeSigner" 48 | }, 49 | "autoload": { 50 | "psr-4": { 51 | "Yiisoft\\Mailer\\SwiftMailer\\": "src" 52 | } 53 | }, 54 | "autoload-dev": { 55 | "psr-4": { 56 | "Yiisoft\\Mailer\\SwiftMailer\\Tests\\": "tests" 57 | } 58 | }, 59 | "extra": { 60 | "config-plugin-options": { 61 | "source-directory": "config" 62 | }, 63 | "config-plugin": { 64 | "params": "params.php", 65 | "common": "common.php" 66 | } 67 | }, 68 | "config": { 69 | "sort-packages": true, 70 | "allow-plugins": { 71 | "infection/extension-installer": true, 72 | "composer/package-versions-deprecated": true 73 | } 74 | }, 75 | "scripts": { 76 | "test": "phpunit --testdox --no-interaction", 77 | "test-watch": "phpunit-watcher watch" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /config/common.php: -------------------------------------------------------------------------------- 1 | [ 22 | 'class' => MessageBodyRenderer::class, 23 | '__construct()' => [ 24 | Reference::to(View::class), 25 | DynamicReference::to(static fn (Aliases $aliases) => new MessageBodyTemplate( 26 | $aliases->get($params['yiisoft/mailer']['messageBodyTemplate']['viewPath']), 27 | )), 28 | ], 29 | ], 30 | 31 | MessageFactoryInterface::class => [ 32 | 'class' => MessageFactory::class, 33 | '__construct()' => [ 34 | Message::class, 35 | ], 36 | ], 37 | 38 | Swift_SmtpTransport::class => [ 39 | 'class' => Swift_SmtpTransport::class, 40 | '__construct()' => [ 41 | $params['swiftmailer/swiftmailer']['SwiftSmtpTransport']['host'], 42 | $params['swiftmailer/swiftmailer']['SwiftSmtpTransport']['port'], 43 | $params['swiftmailer/swiftmailer']['SwiftSmtpTransport']['encryption'], 44 | ], 45 | 'setUsername()' => [$params['swiftmailer/swiftmailer']['SwiftSmtpTransport']['username']], 46 | 'setPassword()' => [$params['swiftmailer/swiftmailer']['SwiftSmtpTransport']['password']], 47 | ], 48 | 49 | Swift_Transport::class => $params['yiisoft/mailer']['useSendmail'] 50 | ? Swift_SendmailTransport::class : Swift_SmtpTransport::class, 51 | 52 | FileMailer::class => [ 53 | 'class' => FileMailer::class, 54 | '__construct()' => [ 55 | 'path' => DynamicReference::to(fn (Aliases $aliases) => $aliases->get( 56 | $params['yiisoft/mailer']['fileMailer']['fileMailerStorage'] 57 | )), 58 | ], 59 | ], 60 | 61 | MailerInterface::class => $params['yiisoft/mailer']['writeToFiles'] 62 | ? FileMailer::class : Mailer::class, 63 | ]; 64 | -------------------------------------------------------------------------------- /config/params.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'messageBodyTemplate' => [ 8 | 'viewPath' => '@resources/mail', 9 | ], 10 | 'fileMailer' => [ 11 | 'fileMailerStorage' => '@runtime/mail', 12 | ], 13 | 'useSendmail' => false, 14 | 'writeToFiles' => true, 15 | ], 16 | 'swiftmailer/swiftmailer' => [ 17 | 'SwiftSmtpTransport' => [ 18 | 'host' => 'smtp.example.com', 19 | 'port' => 25, 20 | 'encryption' => null, 21 | 'username' => 'admin@example.com', 22 | 'password' => '', 23 | ], 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /src/Mailer.php: -------------------------------------------------------------------------------- 1 | swiftMailer = new Swift_Mailer($transport); 42 | 43 | foreach ($plugins as $plugin) { 44 | $this->swiftMailer->registerPlugin($plugin); 45 | } 46 | } 47 | 48 | protected function sendMessage(MessageInterface $message): void 49 | { 50 | /** @var Message $message */ 51 | $sent = $this->swiftMailer->send($message->getSwiftMessage()); 52 | 53 | if ($sent === 0) { 54 | throw new RuntimeException('Unable send message.'); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | swiftMessage = new Swift_Message(); 38 | } 39 | 40 | public function __clone() 41 | { 42 | $this->swiftMessage = clone $this->swiftMessage; 43 | } 44 | 45 | public function getCharset(): string 46 | { 47 | return $this->swiftMessage->getCharset(); 48 | } 49 | 50 | public function withCharset(string $charset): self 51 | { 52 | $new = clone $this; 53 | $new->swiftMessage->setCharset($charset); 54 | return $new; 55 | } 56 | 57 | public function getFrom() 58 | { 59 | return $this->normalizeAddresses($this->swiftMessage->getFrom()); 60 | } 61 | 62 | public function withFrom($from): self 63 | { 64 | $new = clone $this; 65 | $new->swiftMessage->setFrom($from); 66 | return $new; 67 | } 68 | 69 | public function getTo() 70 | { 71 | return $this->normalizeAddresses($this->swiftMessage->getTo()); 72 | } 73 | 74 | public function withTo($to): self 75 | { 76 | $new = clone $this; 77 | $new->swiftMessage->setTo($to); 78 | return $new; 79 | } 80 | 81 | public function getReplyTo() 82 | { 83 | return $this->normalizeAddresses($this->swiftMessage->getReplyTo()); 84 | } 85 | 86 | public function withReplyTo($replyTo): self 87 | { 88 | $new = clone $this; 89 | $new->swiftMessage->setReplyTo($replyTo); 90 | return $new; 91 | } 92 | 93 | public function getCc() 94 | { 95 | return $this->normalizeAddresses($this->swiftMessage->getCc()); 96 | } 97 | 98 | public function withCc($cc): self 99 | { 100 | $new = clone $this; 101 | $new->swiftMessage->setCc($cc); 102 | return $new; 103 | } 104 | 105 | public function getBcc() 106 | { 107 | return $this->normalizeAddresses($this->swiftMessage->getBcc()); 108 | } 109 | 110 | public function withBcc($bcc): self 111 | { 112 | $new = clone $this; 113 | $new->swiftMessage->setBcc($bcc); 114 | return $new; 115 | } 116 | 117 | public function getSubject(): string 118 | { 119 | /** @psalm-suppress RedundantCastGivenDocblockType */ 120 | return (string) $this->swiftMessage->getSubject(); 121 | } 122 | 123 | public function withSubject(string $subject): self 124 | { 125 | $new = clone $this; 126 | $new->swiftMessage->setSubject($subject); 127 | return $new; 128 | } 129 | 130 | public function getDate(): ?DateTimeImmutable 131 | { 132 | return $this->date; 133 | } 134 | 135 | public function withDate(DateTimeInterface $date): self 136 | { 137 | if ($date instanceof DateTime) { 138 | $immutable = new DateTimeImmutable('@' . $date->getTimestamp()); 139 | $date = $immutable->setTimezone($date->getTimezone()); 140 | } 141 | 142 | $new = $this->withHeader('Date', $date->format(DateTimeInterface::RFC2822)); 143 | $new->date = $date; 144 | return $new; 145 | } 146 | 147 | public function getPriority(): int 148 | { 149 | /** @psalm-suppress RedundantCastGivenDocblockType */ 150 | return (int) $this->swiftMessage->getPriority(); 151 | } 152 | 153 | public function withPriority(int $priority): self 154 | { 155 | $new = clone $this; 156 | $new->swiftMessage->setPriority($priority); 157 | return $new; 158 | } 159 | 160 | public function getReturnPath(): string 161 | { 162 | /** @psalm-suppress RedundantCastGivenDocblockType */ 163 | return (string) $this->swiftMessage->getReturnPath(); 164 | } 165 | 166 | public function withReturnPath(string $address): self 167 | { 168 | $new = clone $this; 169 | $new->swiftMessage->setReturnPath($address); 170 | return $new; 171 | } 172 | 173 | public function getSender(): string 174 | { 175 | /** @var array|null $sender */ 176 | $sender = $this->swiftMessage->getSender(); 177 | /** @psalm-suppress RedundantCastGivenDocblockType */ 178 | return empty($sender) ? '' : (string) array_key_first($sender); 179 | } 180 | 181 | public function withSender(string $address): self 182 | { 183 | $new = clone $this; 184 | $new->swiftMessage->setSender($address); 185 | return $new; 186 | } 187 | 188 | public function getTextBody(): string 189 | { 190 | /** @psalm-suppress RedundantCastGivenDocblockType */ 191 | return (string) $this->swiftMessage->getBody(); 192 | } 193 | 194 | public function withTextBody(string $text): self 195 | { 196 | $new = clone $this; 197 | $new->setBody($text, 'text/plain'); 198 | return $new; 199 | } 200 | 201 | public function getHtmlBody(): string 202 | { 203 | /** @psalm-suppress RedundantCastGivenDocblockType */ 204 | return (string) $this->swiftMessage->getBody(); 205 | } 206 | 207 | public function withHtmlBody(string $html): self 208 | { 209 | $new = clone $this; 210 | $new->setBody($html, 'text/html'); 211 | return $new; 212 | } 213 | 214 | public function withAttached(File $file): self 215 | { 216 | $attachment = $file->path() === null 217 | ? new Swift_Attachment($file->content()) 218 | : Swift_Attachment::fromPath($file->path()) 219 | ; 220 | 221 | if (!empty($file->name())) { 222 | $attachment->setFilename($file->name()); 223 | } 224 | 225 | if (!empty($file->contentType())) { 226 | $attachment->setContentType($file->contentType()); 227 | } 228 | 229 | $new = clone $this; 230 | $new->swiftMessage->attach($attachment); 231 | return $new; 232 | } 233 | 234 | public function withEmbedded(File $file): self 235 | { 236 | $embedFile = $file->path() === null 237 | ? new Swift_EmbeddedFile($file->content()) 238 | : Swift_EmbeddedFile::fromPath($file->path()) 239 | ; 240 | 241 | if (!empty($file->name())) { 242 | $embedFile->setFilename($file->name()); 243 | } 244 | 245 | if (!empty($file->contentType())) { 246 | $embedFile->setContentType($file->contentType()); 247 | } 248 | 249 | $new = clone $this; 250 | $new->swiftMessage->embed($embedFile->setId($file->id())); 251 | return $new; 252 | } 253 | 254 | public function getHeader(string $name): array 255 | { 256 | $headerSet = $this->swiftMessage->getHeaders(); 257 | 258 | if (!$headerSet->has($name)) { 259 | return []; 260 | } 261 | 262 | $headers = []; 263 | 264 | /** @var Swift_Mime_Headers_UnstructuredHeader $header */ 265 | foreach ($headerSet->getAll($name) as $header) { 266 | $headers[] = $header->getValue(); 267 | } 268 | 269 | return $headers; 270 | } 271 | 272 | public function withAddedHeader(string $name, string $value): self 273 | { 274 | $new = clone $this; 275 | $new->swiftMessage 276 | ->getHeaders() 277 | ->addTextHeader($name, $value); 278 | return $new; 279 | } 280 | 281 | public function withHeader(string $name, $value): self 282 | { 283 | $new = clone $this; 284 | $headerSet = $new->swiftMessage->getHeaders(); 285 | 286 | if ($headerSet->has($name)) { 287 | $headerSet->remove($name); 288 | } 289 | 290 | foreach ((array) $value as $v) { 291 | $headerSet->addTextHeader($name, $v); 292 | } 293 | 294 | return $new; 295 | } 296 | 297 | public function withHeaders(array $headers): self 298 | { 299 | $new = clone $this; 300 | 301 | foreach ($headers as $name => $value) { 302 | $new = $new->withHeader($name, $value); 303 | } 304 | 305 | return $new; 306 | } 307 | 308 | public function getError(): ?Throwable 309 | { 310 | return $this->error; 311 | } 312 | 313 | public function withError(Throwable $e): self 314 | { 315 | $new = clone $this; 316 | $new->error = $e; 317 | return $new; 318 | } 319 | 320 | public function __toString(): string 321 | { 322 | return $this->swiftMessage->toString(); 323 | } 324 | 325 | /** 326 | * Returns a Swift message instance. 327 | * 328 | * @return Swift_Message Swift message instance. 329 | */ 330 | public function getSwiftMessage(): Swift_Message 331 | { 332 | return $this->swiftMessage; 333 | } 334 | 335 | /** 336 | * Returns the addresses to which a read-receipt will be sent. 337 | * 338 | * @return array|string The receipt receive email addresses. 339 | */ 340 | public function getReadReceiptTo() 341 | { 342 | return $this->normalizeAddresses($this->swiftMessage->getReadReceiptTo()); 343 | } 344 | 345 | /** 346 | * Returns a new instance with the specified ask for a delivery receipt from the recipient to be sent to addresses. 347 | * 348 | * @param string|string[] $addresses The receipt receive email address(es). 349 | * 350 | * @return self 351 | */ 352 | public function withReadReceiptTo($addresses): self 353 | { 354 | $new = clone $this; 355 | $new->swiftMessage->setReadReceiptTo((array) $addresses); 356 | return $new; 357 | } 358 | 359 | /** 360 | * Returns a new instance with the specified attached signers. 361 | * 362 | * @param Swift_Signer[] $signers 363 | * 364 | * @return self 365 | */ 366 | public function withAttachedSigners(array $signers): self 367 | { 368 | $new = clone $this; 369 | 370 | foreach ($signers as $signer) { 371 | $new->swiftMessage->attachSigner($signer); 372 | } 373 | 374 | return $new; 375 | } 376 | 377 | /** 378 | * Sets the message body. 379 | * 380 | * If body is already set and its content type matches given one, it will 381 | * be overridden, if content type miss match the multipart message will be composed. 382 | * 383 | * @param string $body The body content. 384 | * @param string $contentType The body content type. 385 | */ 386 | private function setBody(string $body, string $contentType): void 387 | { 388 | $oldBody = $this->swiftMessage->getBody(); 389 | $charset = $this->swiftMessage->getCharset(); 390 | 391 | if (!empty($oldBody)) { 392 | $oldContentType = $this->swiftMessage->getContentType(); 393 | 394 | if ($oldContentType === $contentType) { 395 | $this->swiftMessage->setBody($body, $contentType); 396 | return; 397 | } 398 | 399 | $this->swiftMessage->setBody(null); 400 | /** @psalm-suppress NullArgument */ 401 | $this->swiftMessage->setContentType(null); 402 | $this->swiftMessage->addPart($oldBody, $oldContentType, $charset); 403 | $this->swiftMessage->addPart($body, $contentType, $charset); 404 | return; 405 | } 406 | 407 | $parts = $this->swiftMessage->getChildren(); 408 | $partFound = false; 409 | 410 | foreach ($parts as $key => $part) { 411 | if ($part instanceof Swift_Mime_MimePart && $part->getContentType() === $contentType) { 412 | $charset = $part->getCharset(); 413 | unset($parts[$key]); 414 | $partFound = true; 415 | break; 416 | } 417 | } 418 | 419 | if (!$partFound) { 420 | $this->swiftMessage->setBody($body, $contentType); 421 | return; 422 | } 423 | 424 | reset($parts); 425 | $this->swiftMessage->setChildren($parts); 426 | $this->swiftMessage->addPart($body, $contentType, $charset); 427 | } 428 | 429 | /** 430 | * Normalizes email addresses and names to the correct format. 431 | * 432 | * @param mixed $addresses 433 | * 434 | * @return array|string 435 | */ 436 | private function normalizeAddresses($addresses) 437 | { 438 | if (empty($addresses)) { 439 | return ''; 440 | } 441 | 442 | if (is_string($addresses)) { 443 | return $addresses; 444 | } 445 | 446 | $normalized = []; 447 | 448 | /** @var mixed $name */ 449 | foreach ((array) $addresses as $address => $name) { 450 | $normalized[(string) $address] = is_string($name) ? $name : ''; 451 | } 452 | 453 | return $normalized; 454 | } 455 | } 456 | --------------------------------------------------------------------------------