From 275b6081e04215eaf7038ca3727a65ab98cebe58 Mon Sep 17 00:00:00 2001 From: brendt Date: Fri, 9 May 2025 08:51:39 +0200 Subject: [PATCH 1/5] wip --- composer.json | 3 ++ packages/mailer/.gitattributes | 10 ++++ packages/mailer/LICENCE.md | 9 ++++ packages/mailer/composer.json | 21 ++++++++ packages/mailer/phpunit.xml | 16 ++++++ packages/mailer/src/Config/mail.config.php | 5 ++ packages/mailer/src/Email.php | 17 +++++++ packages/mailer/src/InvalidFromAddress.php | 13 +++++ packages/mailer/src/MailConfig.php | 14 ++++++ packages/mailer/src/Mailer.php | 54 +++++++++++++++++++++ packages/mailer/src/x-mail.view.php | 15 ++++++ phpunit.xml.dist | 1 + tests/Integration/Mailer/MailerTest.php | 22 +++++++++ tests/Integration/Mailer/test-mail.view.php | 3 ++ 14 files changed, 203 insertions(+) create mode 100644 packages/mailer/.gitattributes create mode 100644 packages/mailer/LICENCE.md create mode 100644 packages/mailer/composer.json create mode 100644 packages/mailer/phpunit.xml create mode 100644 packages/mailer/src/Config/mail.config.php create mode 100644 packages/mailer/src/Email.php create mode 100644 packages/mailer/src/InvalidFromAddress.php create mode 100644 packages/mailer/src/MailConfig.php create mode 100644 packages/mailer/src/Mailer.php create mode 100644 packages/mailer/src/x-mail.view.php create mode 100644 tests/Integration/Mailer/MailerTest.php create mode 100644 tests/Integration/Mailer/test-mail.view.php diff --git a/composer.json b/composer.json index 43c27ac5a..9cad9fe11 100644 --- a/composer.json +++ b/composer.json @@ -84,6 +84,7 @@ "tempest/http": "self.version", "tempest/http-client": "self.version", "tempest/log": "self.version", + "tempest/mailer": "self.version", "tempest/mapper": "self.version", "tempest/reflection": "self.version", "tempest/router": "self.version", @@ -118,6 +119,7 @@ "Tempest\\HttpClient\\": "packages/http-client/src", "Tempest\\Http\\": "packages/http/src", "Tempest\\Log\\": "packages/log/src", + "Tempest\\Mailer\\": "packages/mailer/src", "Tempest\\Mapper\\": "packages/mapper/src", "Tempest\\Reflection\\": "packages/reflection/src", "Tempest\\Router\\": "packages/router/src", @@ -174,6 +176,7 @@ "Tempest\\HttpClient\\Tests\\": "packages/http-client/tests", "Tempest\\Http\\Tests\\": "packages/http/tests", "Tempest\\Log\\Tests\\": "packages/log/tests", + "Tempest\\Mailer\\Tests\\": "packages/mailer/tests", "Tempest\\Mapper\\Tests\\": "packages/mapper/tests", "Tempest\\Reflection\\Tests\\": "packages/reflection/tests", "Tempest\\Router\\Tests\\": "packages/router/tests", diff --git a/packages/mailer/.gitattributes b/packages/mailer/.gitattributes new file mode 100644 index 000000000..950082726 --- /dev/null +++ b/packages/mailer/.gitattributes @@ -0,0 +1,10 @@ +# Exclude build/test files from the release +.github/ export-ignore +tests/ export-ignore +.gitattributes export-ignore +.gitignore export-ignore +phpunit.xml export-ignore +README.md export-ignore + +# Configure diff output for .php and .phar files. +*.php diff=php \ No newline at end of file diff --git a/packages/mailer/LICENCE.md b/packages/mailer/LICENCE.md new file mode 100644 index 000000000..e403836c2 --- /dev/null +++ b/packages/mailer/LICENCE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2024 Brent Roose brendt@stitcher.io + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/packages/mailer/composer.json b/packages/mailer/composer.json new file mode 100644 index 000000000..997827ed7 --- /dev/null +++ b/packages/mailer/composer.json @@ -0,0 +1,21 @@ +{ + "name": "tempest/mailer", + "description": "A component for mails.", + "license": "MIT", + "minimum-stability": "dev", + "require": { + "php": "^8.4", + "tempest/core": "dev-main", + "tempest/view": "dev-main" + }, + "autoload": { + "psr-4": { + "Tempest\\Mailer\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Tempest\\Mailer\\Tests\\": "tests" + } + } +} diff --git a/packages/mailer/phpunit.xml b/packages/mailer/phpunit.xml new file mode 100644 index 000000000..dbc66809c --- /dev/null +++ b/packages/mailer/phpunit.xml @@ -0,0 +1,16 @@ + + + + + tests + + + + + src + + + + + + diff --git a/packages/mailer/src/Config/mail.config.php b/packages/mailer/src/Config/mail.config.php new file mode 100644 index 000000000..a6843fc67 --- /dev/null +++ b/packages/mailer/src/Config/mail.config.php @@ -0,0 +1,5 @@ +from = $from ?? env('MAIL_FROM'); + } +} \ No newline at end of file diff --git a/packages/mailer/src/Mailer.php b/packages/mailer/src/Mailer.php new file mode 100644 index 000000000..085d4c1fd --- /dev/null +++ b/packages/mailer/src/Mailer.php @@ -0,0 +1,54 @@ +renderer->render(view($path, ...$params)); + + [$header, $body] = explode(PHP_EOL, $html, 2); + + $email = $this->makeEmail($header, $body); + + $this->sendEmail($email); + } + + public function sendEmail(Email $email): void + { + if ($email->from === null) { + throw new InvalidFromAddress(); + } + + ld($email); + } + + private function makeEmail(string $header, string $body): Email + { + $parsedHeaders = []; + + $headers = explode(';', $header); + + foreach ($headers as $header) { + [$name, $value] = explode(':', $header); + + $parsedHeaders[trim($name)] = trim($value); + } + + $data = [ + ...$parsedHeaders, + 'body' => $body, + ]; + + return new Email(...$data); + } +} \ No newline at end of file diff --git a/packages/mailer/src/x-mail.view.php b/packages/mailer/src/x-mail.view.php new file mode 100644 index 000000000..c35d64a74 --- /dev/null +++ b/packages/mailer/src/x-mail.view.php @@ -0,0 +1,15 @@ +from: {{ $from }}; to: {{ $to }}; subject: {{ $subject }}; async: {{ ($async ?? true) ? 'true' : 'false' }} + + + + + + + + +
{{ $pretext }}
+ + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ae98afb83..f3cbf92a2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -33,5 +33,6 @@ + diff --git a/tests/Integration/Mailer/MailerTest.php b/tests/Integration/Mailer/MailerTest.php new file mode 100644 index 000000000..750090702 --- /dev/null +++ b/tests/Integration/Mailer/MailerTest.php @@ -0,0 +1,22 @@ +container->get(Mailer::class); + + $mailer->send( + __DIR__ . '/test-mail.view.php', + from: 'sender@tempestphp.com', + to: 'brendt@stitcher.io', + subject: 'Hello there', + name: 'Brent', + ); + } +} \ No newline at end of file diff --git a/tests/Integration/Mailer/test-mail.view.php b/tests/Integration/Mailer/test-mail.view.php new file mode 100644 index 000000000..238d79090 --- /dev/null +++ b/tests/Integration/Mailer/test-mail.view.php @@ -0,0 +1,3 @@ + + Hello {{ $name }} + \ No newline at end of file From d62248416a65acbae0c29da03677195bf57c40e1 Mon Sep 17 00:00:00 2001 From: brendt Date: Fri, 9 May 2025 08:58:19 +0200 Subject: [PATCH 2/5] Add attachments --- packages/mailer/src/Mailer.php | 6 ++++++ packages/mailer/src/x-mail.view.php | 2 +- tests/Integration/Mailer/Fixtures/attachment.txt | 1 + tests/Integration/Mailer/MailerTest.php | 4 ++++ tests/Integration/Mailer/test-mail.view.php | 2 +- 5 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/Integration/Mailer/Fixtures/attachment.txt diff --git a/packages/mailer/src/Mailer.php b/packages/mailer/src/Mailer.php index 085d4c1fd..18b1f2227 100644 --- a/packages/mailer/src/Mailer.php +++ b/packages/mailer/src/Mailer.php @@ -39,11 +39,17 @@ private function makeEmail(string $header, string $body): Email $headers = explode(';', $header); foreach ($headers as $header) { + if (trim($header) === '') { + continue; + } + [$name, $value] = explode(':', $header); $parsedHeaders[trim($name)] = trim($value); } + $parsedHeaders['attachments'] = explode(',', $parsedHeaders['attachments'] ?? ''); + $data = [ ...$parsedHeaders, 'body' => $body, diff --git a/packages/mailer/src/x-mail.view.php b/packages/mailer/src/x-mail.view.php index c35d64a74..5b46c3221 100644 --- a/packages/mailer/src/x-mail.view.php +++ b/packages/mailer/src/x-mail.view.php @@ -1,4 +1,4 @@ -from: {{ $from }}; to: {{ $to }}; subject: {{ $subject }}; async: {{ ($async ?? true) ? 'true' : 'false' }} +from: {{ $from }}; to: {{ $to }}; subject: {{ $subject }}; async: {{ ($async ?? true) ? 'true' : 'false' }}; attachments: {{ implode(',', $attachments ?? []) }}; diff --git a/tests/Integration/Mailer/Fixtures/attachment.txt b/tests/Integration/Mailer/Fixtures/attachment.txt new file mode 100644 index 000000000..40816a2b5 --- /dev/null +++ b/tests/Integration/Mailer/Fixtures/attachment.txt @@ -0,0 +1 @@ +Hi \ No newline at end of file diff --git a/tests/Integration/Mailer/MailerTest.php b/tests/Integration/Mailer/MailerTest.php index 750090702..40f1167b4 100644 --- a/tests/Integration/Mailer/MailerTest.php +++ b/tests/Integration/Mailer/MailerTest.php @@ -17,6 +17,10 @@ public function test_mailer(): void to: 'brendt@stitcher.io', subject: 'Hello there', name: 'Brent', + files: [ + __DIR__ . '/Fixtures/attachment.txt', + __DIR__ . '/test-mail.view.php', + ] ); } } \ No newline at end of file diff --git a/tests/Integration/Mailer/test-mail.view.php b/tests/Integration/Mailer/test-mail.view.php index 238d79090..ac43caa64 100644 --- a/tests/Integration/Mailer/test-mail.view.php +++ b/tests/Integration/Mailer/test-mail.view.php @@ -1,3 +1,3 @@ - + Hello {{ $name }} \ No newline at end of file From daab6d2ae304858852e8ba4339ccb30d5284523e Mon Sep 17 00:00:00 2001 From: brendt Date: Fri, 9 May 2025 09:06:01 +0200 Subject: [PATCH 3/5] wip --- packages/mailer/src/Mailer.php | 8 +++++++- tests/Integration/Mailer/MailerTest.php | 11 +++++++---- tests/Integration/Mailer/test-mail.view.php | 15 +++++++++++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/mailer/src/Mailer.php b/packages/mailer/src/Mailer.php index 18b1f2227..b1ef311dd 100644 --- a/packages/mailer/src/Mailer.php +++ b/packages/mailer/src/Mailer.php @@ -45,7 +45,13 @@ private function makeEmail(string $header, string $body): Email [$name, $value] = explode(':', $header); - $parsedHeaders[trim($name)] = trim($value); + $value = trim($value); + + if ($value === '') { + $value = null; + } + + $parsedHeaders[trim($name)] = $value; } $parsedHeaders['attachments'] = explode(',', $parsedHeaders['attachments'] ?? ''); diff --git a/tests/Integration/Mailer/MailerTest.php b/tests/Integration/Mailer/MailerTest.php index 40f1167b4..fdc0844fe 100644 --- a/tests/Integration/Mailer/MailerTest.php +++ b/tests/Integration/Mailer/MailerTest.php @@ -11,12 +11,15 @@ public function test_mailer(): void { $mailer = $this->container->get(Mailer::class); + $user = (object) [ + 'email' => 'brendt@stitcher.io', + 'name' => 'Brent Roose', + 'first_name' => 'Brent', + ]; + $mailer->send( __DIR__ . '/test-mail.view.php', - from: 'sender@tempestphp.com', - to: 'brendt@stitcher.io', - subject: 'Hello there', - name: 'Brent', + user: $user, files: [ __DIR__ . '/Fixtures/attachment.txt', __DIR__ . '/test-mail.view.php', diff --git a/tests/Integration/Mailer/test-mail.view.php b/tests/Integration/Mailer/test-mail.view.php index ac43caa64..a9fc4dbd8 100644 --- a/tests/Integration/Mailer/test-mail.view.php +++ b/tests/Integration/Mailer/test-mail.view.php @@ -1,3 +1,14 @@ - - Hello {{ $name }} + + Hello {{ $user->first_name }} + + Thanks for checking out Tempest! + + See you soon! + + — The Tempest Team \ No newline at end of file From 03e98a4010bb703db7a0889b3e0960429d092efa Mon Sep 17 00:00:00 2001 From: brendt Date: Fri, 9 May 2025 09:10:05 +0200 Subject: [PATCH 4/5] wip --- packages/mailer/src/Mailer.php | 25 ++++++++----------------- packages/mailer/src/x-mail.view.php | 2 +- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/mailer/src/Mailer.php b/packages/mailer/src/Mailer.php index b1ef311dd..677ca53db 100644 --- a/packages/mailer/src/Mailer.php +++ b/packages/mailer/src/Mailer.php @@ -16,9 +16,9 @@ public function send(string $path, ...$params): void { $html = $this->renderer->render(view($path, ...$params)); - [$header, $body] = explode(PHP_EOL, $html, 2); + [$headerString, $body] = explode(PHP_EOL, $html, 2); - $email = $this->makeEmail($header, $body); + $email = $this->makeEmail($headerString, $body); $this->sendEmail($email); } @@ -32,26 +32,17 @@ public function sendEmail(Email $email): void ld($email); } - private function makeEmail(string $header, string $body): Email + private function makeEmail(string $headerString, string $body): Email { $parsedHeaders = []; - $headers = explode(';', $header); + preg_match_all('/(?
\w+)="(?.*?)"/', $headerString, $headers); - foreach ($headers as $header) { - if (trim($header) === '') { - continue; - } + foreach ($headers[0] as $i => $line) { + $header = $headers['header'][$i]; + $value = $headers['value'][$i]; - [$name, $value] = explode(':', $header); - - $value = trim($value); - - if ($value === '') { - $value = null; - } - - $parsedHeaders[trim($name)] = $value; + $parsedHeaders[$header] = $value; } $parsedHeaders['attachments'] = explode(',', $parsedHeaders['attachments'] ?? ''); diff --git a/packages/mailer/src/x-mail.view.php b/packages/mailer/src/x-mail.view.php index 5b46c3221..945d142b9 100644 --- a/packages/mailer/src/x-mail.view.php +++ b/packages/mailer/src/x-mail.view.php @@ -1,4 +1,4 @@ -from: {{ $from }}; to: {{ $to }}; subject: {{ $subject }}; async: {{ ($async ?? true) ? 'true' : 'false' }}; attachments: {{ implode(',', $attachments ?? []) }}; +from="{{ $from }}" to="{{ $to }}" subject="{{ $subject }}" async="{{ ($async ?? true) ? 'true' : 'false' }}" attachments="{{ implode(',', $attachments ?? []) }}" From a8f7262cc80329f3c53185a485c8506ea2fd0d65 Mon Sep 17 00:00:00 2001 From: brendt Date: Fri, 9 May 2025 09:10:40 +0200 Subject: [PATCH 5/5] wip --- packages/mailer/src/Config/mail.config.php | 2 +- packages/mailer/src/Email.php | 2 +- packages/mailer/src/InvalidFromAddress.php | 2 +- packages/mailer/src/MailConfig.php | 2 +- packages/mailer/src/Mailer.php | 3 ++- tests/Integration/Mailer/MailerTest.php | 4 ++-- tests/Integration/Mailer/test-mail.view.php | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/mailer/src/Config/mail.config.php b/packages/mailer/src/Config/mail.config.php index a6843fc67..ad872db5a 100644 --- a/packages/mailer/src/Config/mail.config.php +++ b/packages/mailer/src/Config/mail.config.php @@ -2,4 +2,4 @@ use Tempest\Mailer\MailConfig; -return new MailConfig(); \ No newline at end of file +return new MailConfig(); diff --git a/packages/mailer/src/Email.php b/packages/mailer/src/Email.php index 895ebb72d..c61afd27e 100644 --- a/packages/mailer/src/Email.php +++ b/packages/mailer/src/Email.php @@ -14,4 +14,4 @@ public function __construct( public array $attachments = [], public bool $async = false, ) {} -} \ No newline at end of file +} diff --git a/packages/mailer/src/InvalidFromAddress.php b/packages/mailer/src/InvalidFromAddress.php index e54e41a59..4659585e5 100644 --- a/packages/mailer/src/InvalidFromAddress.php +++ b/packages/mailer/src/InvalidFromAddress.php @@ -10,4 +10,4 @@ public function __construct() { parent::__construct("There's no valid from address configured."); } -} \ No newline at end of file +} diff --git a/packages/mailer/src/MailConfig.php b/packages/mailer/src/MailConfig.php index 4311da2c4..747fb1875 100644 --- a/packages/mailer/src/MailConfig.php +++ b/packages/mailer/src/MailConfig.php @@ -11,4 +11,4 @@ public function __construct( ) { $this->from = $from ?? env('MAIL_FROM'); } -} \ No newline at end of file +} diff --git a/packages/mailer/src/Mailer.php b/packages/mailer/src/Mailer.php index 677ca53db..be838ec21 100644 --- a/packages/mailer/src/Mailer.php +++ b/packages/mailer/src/Mailer.php @@ -3,6 +3,7 @@ namespace Tempest\Mailer; use Tempest\View\ViewRenderer; + use function Tempest\view; final class Mailer @@ -54,4 +55,4 @@ private function makeEmail(string $headerString, string $body): Email return new Email(...$data); } -} \ No newline at end of file +} diff --git a/tests/Integration/Mailer/MailerTest.php b/tests/Integration/Mailer/MailerTest.php index fdc0844fe..a05d398eb 100644 --- a/tests/Integration/Mailer/MailerTest.php +++ b/tests/Integration/Mailer/MailerTest.php @@ -23,7 +23,7 @@ public function test_mailer(): void files: [ __DIR__ . '/Fixtures/attachment.txt', __DIR__ . '/test-mail.view.php', - ] + ], ); } -} \ No newline at end of file +} diff --git a/tests/Integration/Mailer/test-mail.view.php b/tests/Integration/Mailer/test-mail.view.php index a9fc4dbd8..b577bbc4b 100644 --- a/tests/Integration/Mailer/test-mail.view.php +++ b/tests/Integration/Mailer/test-mail.view.php @@ -11,4 +11,4 @@ See you soon! — The Tempest Team - \ No newline at end of file +