├── .vscode
└── settings.json
├── CHANGELOG.md
├── psalm.xml.dist
├── LICENSE.md
├── .php_cs.dist.php
├── composer.json
├── README.md
└── src
└── MailpaceSwiftmailerTransport.php
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "Mailpace",
4 | "Packagist",
5 | "htmlbody",
6 | "replyto",
7 | "textbody"
8 | ]
9 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to `mailpace-swiftmailer` will be documented in this file.
4 |
5 | ## 1.0.0 - 2021-05-28
6 |
7 | - initial release
8 |
9 | # 1.0.3 - 2021-01-27
10 |
11 | - Move from OhMySMTP to MailPace
12 |
13 | # 1.0.4 - 2021-05-09
14 |
15 | - Move email-validator dependency to developer dependencies
--------------------------------------------------------------------------------
/psalm.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) MailPace
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.php_cs.dist.php:
--------------------------------------------------------------------------------
1 | in([
5 | __DIR__ . '/src',
6 | __DIR__ . '/tests',
7 | ])
8 | ->name('*.php')
9 | ->notName('*.blade.php')
10 | ->ignoreDotFiles(true)
11 | ->ignoreVCS(true);
12 |
13 | return (new PhpCsFixer\Config())
14 | ->setRules([
15 | '@PSR2' => true,
16 | 'array_syntax' => ['syntax' => 'short'],
17 | 'ordered_imports' => ['sort_algorithm' => 'alpha'],
18 | 'no_unused_imports' => true,
19 | 'not_operator_with_successor_space' => true,
20 | 'trailing_comma_in_multiline' => true,
21 | 'phpdoc_scalar' => true,
22 | 'unary_operator_spaces' => true,
23 | 'binary_operator_spaces' => true,
24 | 'blank_line_before_statement' => [
25 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
26 | ],
27 | 'phpdoc_single_line_var_spacing' => true,
28 | 'phpdoc_var_without_name' => true,
29 | 'class_attributes_separation' => [
30 | 'elements' => [
31 | 'method' => 'one',
32 | ],
33 | ],
34 | 'method_argument_space' => [
35 | 'on_multiline' => 'ensure_fully_multiline',
36 | 'keep_multiple_spaces_after_comma' => true,
37 | ],
38 | 'single_trait_insert_per_statement' => true,
39 | ])
40 | ->setFinder($finder);
41 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mailpace/mailpace-swiftmailer",
3 | "description": "The official Swiftmailer transport for MailPace",
4 | "keywords": [
5 | "MailPace",
6 | "mailpace-swiftmailer",
7 | "swiftmailer",
8 | "transactional",
9 | "email",
10 | "transactional email"
11 | ],
12 | "homepage": "https://github.com/mailpace/mailpace-swiftmailer",
13 | "license": "MIT",
14 | "authors": [
15 | {
16 | "name": "Paul",
17 | "email": "support@mailpace.com",
18 | "role": "Developer"
19 | }
20 | ],
21 | "require": {
22 | "php": ">=7.3",
23 | "guzzlehttp/guzzle": ">=6.0",
24 | "swiftmailer/swiftmailer": ">=6.2.7"
25 | },
26 | "require-dev": {
27 | "egulias/email-validator": "^2.1.10|^3.1",
28 | "friendsofphp/php-cs-fixer": "^2.17",
29 | "phpunit/phpunit": "^9.5",
30 | "spatie/ray": "^1.10",
31 | "vimeo/psalm": "^4.3"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "Mailpace\\MailpaceSwiftmailer\\": "src"
36 | }
37 | },
38 | "autoload-dev": {
39 | "psr-4": {
40 | "Mailpace\\MailpaceSwiftmailer\\Tests\\": "tests"
41 | }
42 | },
43 | "scripts": {
44 | "psalm": "vendor/bin/psalm",
45 | "test": "vendor/bin/phpunit",
46 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage",
47 | "format": "vendor/bin/php-cs-fixer fix --allow-risky=yes"
48 | },
49 | "config": {
50 | "sort-packages": true
51 | },
52 | "minimum-stability": "dev",
53 | "prefer-stable": true
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The official Swiftmailer transport for MailPace
2 |
3 | [](https://packagist.org/packages/mailpace/mailpace-swiftmailer)
4 | [](https://github.com/mailpace/mailpace-swiftmailer/actions?query=workflow%3ATests+branch%3Amain)
5 | [](https://github.com/mailpace/mailpace-swiftmailer/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain)
6 | [](https://packagist.org/packages/mailpace/mailpace-swiftmailer)
7 |
8 |
9 | [MailPace](https://mailpace.com) lets you send transactional emails from your app over an easy to use API.
10 |
11 | This MailPace PHP Package is a transport for SwiftMailer to send emails via [MailPace](https://mailpace.com) to make sending emails from PHP apps super simple. You can use this with popular frameworks such as Laravel, Codeigniter and Symfony to send transactional emails, or with a standalone PHP app.
12 |
13 | This package uses the MailPace HTTPS [/send endpoint](https://docs.mailpace.com/reference/send) to send the email - which is generally faster and more reliable than SMTP, although you can of course use SMTP to send out emails from your PHP app without installing this package if you prefer.
14 |
15 | ## Pre-requisites
16 |
17 | You will need an MailPace account with a verified domain and organization with an active plan.
18 |
19 | ## Installation
20 |
21 | Install the package via composer:
22 |
23 | ```bash
24 | composer require mailpace/mailpace-swiftmailer
25 | ```
26 | ### Account Setup
27 |
28 | Set up an account at [MailPace](https://app.mailpace.com/users/sign_up) and complete the Onboarding steps
29 |
30 | ### Configure the Package
31 |
32 | First you will need to retrieve your API token for your sending domain from [MailPace](https://app.mailpace.com). You can find it under Organization -> Domain -> API Tokens
33 |
34 | You'll need to store this API token somewhere in your app/runtime, we recommend Environment Variables for this, and the examples below assume that you have an environment variable called `OHMYSMTP_API_TOKEN` that contains your API token
35 |
36 | #### Sending without a framework
37 |
38 | ```php
39 | setFrom(['php@yourdomain.com' => 'Your Name'])
47 | ->setTo(['someone@example.com'])
48 | ->setBody('HTML content
', 'text/html')
49 | ->addPart('Text Body','text/plain');
50 |
51 | // Attachments
52 | $data = 'Raw Attachment Data';
53 | $attachment = new Swift_Attachment($data, 'attachment.txt', 'text/plain');
54 | $message->attach($attachment);
55 |
56 | // Email Tags
57 | $headers = $message->getHeaders();
58 | $headers->addTextHeader('MailPace-Tag', 'tag-1');
59 | $headers->addTextHeader('MailPace-Tag', 'tag with spaces');
60 |
61 | $mailer->send($message);
62 | ?>
63 | ```
64 |
65 | #### Sending with Laravel
66 |
67 | To send with Laravel you need to make a few small tweaks, but it really only takes a moment.
68 |
69 | 1. Add mailpace to the `config/mail.php` configuration file:
70 |
71 | ```php
72 | 'mailpace' => [
73 | 'transport' => 'mailpace',
74 | ],
75 | ```
76 |
77 |
78 | 2. Add the following to your `config/services.php` configuration file:
79 |
80 | ```php
81 | 'mailpace' => [
82 | 'apiToken' => env('OHMYSMTP_API_TOKEN'),
83 | ]
84 | ```
85 |
86 | 3. In `config/app.php`, add the following to the providers array:
87 |
88 | ```php
89 | App\Providers\MailpaceServiceProvider::class,
90 | ```
91 |
92 | and remove / comment out the line:
93 |
94 | ```php
95 | Illuminate\Mail\MailServiceProvider::class,
96 | ```
97 |
98 | 4. In your `.env` file (or wherever you store environment variables), change the `MAIL_MAILER` variable as follows:
99 |
100 | `MAIL_MAILER=mailpace`
101 |
102 | 5. Create a new file called `MailpaceServiceProvider.php` in `App/Providers` with the following contents:
103 |
104 | ```php
105 | app->singleton('mail.manager', function ($app) {
118 | $manager = new MailManager($app);
119 | $this->registerOhMySmtpTransport($manager);
120 | return $manager;
121 | });
122 | }
123 |
124 | protected function registerOhMySmtpTransport(MailManager $manager) {
125 | $manager->extend('mailpace', function ($config) {
126 | if (! isset($config['apiToken'])) {
127 | $config = $this->app['config']->get('services.mailpace', []);
128 | }
129 | return new MailpaceSwiftmailerTransport($config['apiToken']);
130 | });
131 | }
132 | }
133 |
134 | ```
135 | After completing the above steps, all email will be sent via MailPace.
136 |
137 | ## Support
138 |
139 | For support please check the [MailPace Documentation](https://docs.mailpace.com) or contact us at support@mailpace.com
140 |
141 | ## Contributing
142 |
143 | Please ensure to add a test for any changes. To run the tests:
144 |
145 | `composer test`
146 |
147 | Pull requests always welcome
148 |
149 | ## License
150 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
--------------------------------------------------------------------------------
/src/MailpaceSwiftmailerTransport.php:
--------------------------------------------------------------------------------
1 | version = phpversion();
38 | $this->os = PHP_OS;
39 | $this->_eventDispatcher = \Swift_DependencyContainer::getInstance()->lookup('transport.eventdispatcher');
40 | $this->apiToken = $apiToken;
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
47 | {
48 | $client = $this->getHttpClient();
49 |
50 | if ($sendEvent = $this->_eventDispatcher->createSendEvent($this, $message)) {
51 | $this->_eventDispatcher->dispatchEvent($sendEvent, 'beforeSendPerformed');
52 | if ($sendEvent->bubbleCancelled()) {
53 | return 0;
54 | }
55 | }
56 |
57 | $php_version = $this->version;
58 |
59 | $response = $client->request('POST', 'https://app.mailpace.com/api/v1/send', [
60 | 'json' => $this->convertToOms($message),
61 | 'headers' => [
62 | 'MailPace-Server-Token' => $this->apiToken,
63 | 'Content-Type' => 'application/json',
64 | 'Accept' => 'application/json',
65 | 'User-Agent' => "MailPace Swiftmailer Package (PHP v$php_version)",
66 | ],
67 | 'http_errors' => false, // Errors are handled by Swiftmailer event listener
68 | ]);
69 |
70 | $success = $response->getStatusCode() === 200;
71 |
72 | if ($responseEvent = $this->_eventDispatcher->createResponseEvent($this, $response->getBody()->__toString(), $success)) {
73 | $this->_eventDispatcher->dispatchEvent($responseEvent, 'responseReceived');
74 | }
75 |
76 | $sendEvent->setResult($success ? \Swift_Events_SendEvent::RESULT_SUCCESS : \Swift_Events_SendEvent::RESULT_FAILED);
77 | $this->_eventDispatcher->dispatchEvent($sendEvent, 'sendPerformed');
78 |
79 | return $success ? $this->countSent($message) : 0;
80 | }
81 |
82 | /**
83 | * Get the number of delivered addresses
84 | *
85 | * @param Swift_Mime_SimpleMessage $message
86 | * @return int
87 | */
88 | protected function countSent(Swift_Mime_SimpleMessage $message)
89 | {
90 | return count(
91 | array_merge(
92 | (array) $message->getTo(),
93 | (array) $message->getCc(),
94 | (array) $message->getBcc()
95 | )
96 | );
97 | }
98 |
99 | /**
100 | * Prep email by converting from emails from dict to array
101 | *
102 | * @param array $emails
103 | * @return array
104 | */
105 | protected function prepareEmailAddresses($emails)
106 | {
107 | $convertedEmails = [];
108 | foreach ($emails as $email => $name) {
109 | $convertedEmails[] = $name ? $name . " <{$email}>" : $email;
110 | }
111 |
112 | return $convertedEmails;
113 | }
114 |
115 | /**
116 | * Extract the MIME type requested
117 | *
118 | * @param Swift_Mime_SimpleMessage $message
119 | * @param string $mimeType
120 | * @return Swift_Mime_MimePart|null
121 | */
122 | protected function getMIMEPart(Swift_Mime_SimpleMessage $message, $mimeType)
123 | {
124 | foreach ($message->getChildren() as $part) {
125 | if (strpos($part->getContentType(), $mimeType) === 0 && ! ($part instanceof \Swift_Mime_Attachment)) {
126 | return $part;
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * Convert a Swift MIME Message to an MailPace API object
133 | * See https://docs.mailpace.com/reference/send for details
134 | *
135 | * @param Swift_Mime_SimpleMessage $message
136 | * @return array
137 | */
138 | protected function convertToOms(Swift_Mime_SimpleMessage $message)
139 | {
140 | $payload = [];
141 |
142 | $this->addAddresses($payload, $message);
143 | $this->addSubject($payload, $message);
144 | $this->processMessageParts($payload, $message);
145 | if ($message->getHeaders()) {
146 | $this->processTagsFromHeaders($payload, $message);
147 | }
148 |
149 | return $payload;
150 | }
151 |
152 | /**
153 | * Add SwiftMailer recipients to MailPace payload
154 | *
155 | * @param array $payload
156 | * @param Swift_Mime_SimpleMessage $message
157 | */
158 | protected function addAddresses(&$payload, $message)
159 | {
160 | $payload['from'] = join(',', $this->prepareEmailAddresses($message->getFrom()));
161 | if ($to = $message->getTo()) {
162 | $payload['to'] = join(',', $this->prepareEmailAddresses($to));
163 | }
164 | if ($cc = $message->getCc()) {
165 | $payload['cc'] = join(',', $this->prepareEmailAddresses($cc));
166 | }
167 | if ($bcc = $message->getBcc()) {
168 | $payload['bcc'] = join(',', $this->prepareEmailAddresses($bcc));
169 | }
170 | if ($reply_to = $message->getReplyTo()) {
171 | /**
172 | * @psalm-suppress InvalidArgument
173 | */
174 | $payload['replyto'] = join(',', $this->prepareEmailAddresses($reply_to));
175 | }
176 | }
177 |
178 | /**
179 | * Add swiftmailer subject to MailPace payload
180 | *
181 | * @param array $payload
182 | * @param Swift_Mime_SimpleMessage $message
183 | */
184 | protected function addSubject(&$payload, $message)
185 | {
186 | $payload['subject'] = $message->getSubject();
187 | }
188 |
189 | /**
190 | * Turn SwiftMailer MIME parts into htmlbody, textbody and attachment array
191 | *
192 | * @param array $payload
193 | * @param Swift_Mime_SimpleMessage $message
194 | */
195 | protected function processMessageParts(&$payload, $message)
196 | {
197 | switch ($message->getContentType()) {
198 | case 'text/html':
199 | case 'multipart/alternative':
200 | case 'multipart/mixed':
201 | $payload['htmlbody'] = $message->getBody();
202 |
203 | break;
204 | default:
205 | $payload['textbody'] = $message->getBody();
206 |
207 | break;
208 | }
209 |
210 | // If there are other html or text parts include them
211 | if ($plain = $this->getMIMEPart($message, 'text/plain')) {
212 | $payload['textbody'] = $plain->getBody();
213 | }
214 | if ($html = $this->getMIMEPart($message, 'text/html')) {
215 | $payload['htmlbody'] = $html->getBody();
216 | }
217 |
218 | // Attachments
219 | if ($message->getChildren()) {
220 | $payload['attachments'] = [];
221 | foreach ($message->getChildren() as $attachment) {
222 | if (is_object($attachment) and $attachment instanceof \Swift_Mime_Attachment) {
223 | $attachments = [
224 | 'name' => $attachment->getFilename(),
225 | 'content' => base64_encode($attachment->getBody()),
226 | 'content_type' => $attachment->getContentType(),
227 | ];
228 | if ($attachment->getDisposition() != 'attachment' && $attachment->getId() != null) {
229 | $attachments['cid'] = 'cid:' . $attachment->getId();
230 | }
231 | $payload['attachments'][] = $attachments;
232 | }
233 | }
234 | }
235 | }
236 |
237 | /**
238 | * Move MailPace-Tags from headers into the API payload
239 | *
240 | * @param array $payload
241 | * @param Swift_Mime_SimpleMessage $message
242 | */
243 | protected function processTagsFromHeaders(&$payload, $message)
244 | {
245 | $payload['tags'] = [];
246 | foreach ($message->getHeaders()->getAll() as $value) {
247 | $fieldName = $value->getFieldName();
248 | if ($fieldName == 'MailPace-Tag') {
249 | array_push($payload['tags'], $value->getValue());
250 | }
251 | }
252 | }
253 |
254 | /**
255 | * {@inheritdoc}
256 | */
257 | public function registerPlugin(Swift_Events_EventListener $plugin)
258 | {
259 | $this->_eventDispatcher->bindEventListener($plugin);
260 | }
261 |
262 | /**
263 | * Get Guzzle HTTP client instance
264 | *
265 | * @return \GuzzleHttp\Client
266 | */
267 | protected function getHttpClient()
268 | {
269 | return new Client;
270 | }
271 |
272 | /**
273 | * {@inheritdoc}
274 | */
275 | public function isStarted()
276 | {
277 | return true;
278 | }
279 |
280 | /**
281 | * {@inheritdoc}
282 | */
283 | public function start()
284 | {
285 | return true;
286 | }
287 |
288 | /**
289 | * {@inheritdoc}
290 | */
291 | public function stop()
292 | {
293 | return true;
294 | }
295 |
296 | /**
297 | * Ping
298 | *
299 | * @return bool
300 | */
301 | public function ping()
302 | {
303 | return true;
304 | }
305 | }
306 |
--------------------------------------------------------------------------------