diff --git a/composer.json b/composer.json index 1be3425c..baa56138 100644 --- a/composer.json +++ b/composer.json @@ -40,9 +40,8 @@ "psr/http-server-middleware": "^1.0", "psr/log": "^1.1", "respect/validation": "^1.1", - "swiftmailer/swiftmailer": "^6.2", "symfony/http-foundation": "^6.0", - "symfony/mime": "^6.0", + "symfony/mailer": "^6.0", "symfony/psr-http-message-bridge": "^2.1", "twig/twig": "^3.3", "vlucas/phpdotenv": "^5.4" diff --git a/composer.lock b/composer.lock index 86effbc2..88ca7b0f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6382e7e9ba81aa4f0146649008ae6f2d", + "content-hash": "427d326e0e3e19debb1d2c885f2cfbea", "packages": [ { "name": "composer/package-versions-deprecated", @@ -2208,6 +2208,56 @@ }, "time": "2021-11-05T16:50:12+00:00" }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, { "name": "psr/http-client", "version": "1.0.1", @@ -2694,82 +2744,6 @@ }, "time": "2019-05-28T06:10:06+00:00" }, - { - "name": "swiftmailer/swiftmailer", - "version": "v6.3.0", - "source": { - "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.0|^3.1", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.4" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.2-dev" - } - }, - "autoload": { - "files": [ - "lib/swift_required.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Corbyn" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" - ], - "support": { - "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", - "type": "tidelift" - } - ], - "abandoned": "symfony/mailer", - "time": "2021-10-18T15:26:12+00:00" - }, { "name": "symfony/console", "version": "v5.4.1", @@ -2936,6 +2910,168 @@ ], "time": "2021-11-01T23:48:49+00:00" }, + { + "name": "symfony/event-dispatcher", + "version": "v6.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4f06d19a5f78087061f9de6df3269c139c3d289d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4f06d19a5f78087061f9de6df3269c139c3d289d", + "reference": "4f06d19a5f78087061f9de6df3269c139c3d289d", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/event-dispatcher-contracts": "^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-12-08T15:13:44+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "aa5422287b75594b90ee9cd807caf8f0df491385" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/aa5422287b75594b90ee9cd807caf8f0df491385", + "reference": "aa5422287b75594b90ee9cd807caf8f0df491385", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-15T12:33:35+00:00" + }, { "name": "symfony/http-foundation", "version": "v6.0.1", @@ -3008,6 +3144,80 @@ ], "time": "2021-12-09T12:47:37+00:00" }, + { + "name": "symfony/mailer", + "version": "v6.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "d0547edd8de59bb30c3fe9db14473ed1fee5ef8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d0547edd8de59bb30c3fe9db14473ed1fee5ef8a", + "reference": "d0547edd8de59bb30c3fe9db14473ed1fee5ef8a", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3", + "php": ">=8.0.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/service-contracts": "^1.1|^2|^3" + }, + "conflict": { + "symfony/http-kernel": "<5.4" + }, + "require-dev": { + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/messenger": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.0.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-12-08T15:13:44+00:00" + }, { "name": "symfony/mime", "version": "v6.0.1", @@ -3168,86 +3378,6 @@ ], "time": "2021-02-19T12:13:01+00:00" }, - { - "name": "symfony/polyfill-iconv", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/63b5bb7db83e5673936d6e3b8b3e022ff6474933", - "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-iconv": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Iconv extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "iconv", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T09:27:20+00:00" - }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.23.1", diff --git a/config/config.default.php b/config/config.default.php index f0be4aa2..2ce3a12d 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -49,7 +49,7 @@ return [ // Email config 'email' => [ - // Can be mail, smtp, sendmail or log + // Can be mail, smtp, sendmail or log or an symfony mailer dsn string like smtps://[usr]:[pass]@smtp.foo.bar:465 'driver' => env('MAIL_DRIVER', 'mail'), 'from' => [ // From address of all emails @@ -59,8 +59,8 @@ return [ 'host' => env('MAIL_HOST', 'localhost'), 'port' => env('MAIL_PORT', 587), - // Transport encryption like tls (for starttls) or ssl - 'encryption' => env('MAIL_ENCRYPTION', null), + // If tls transport encryption should be used + 'tls' => env('MAIL_TLS', null), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'sendmail' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'), diff --git a/includes/controller/user_angeltypes_controller.php b/includes/controller/user_angeltypes_controller.php index 06abc860..0b6c29ad 100644 --- a/includes/controller/user_angeltypes_controller.php +++ b/includes/controller/user_angeltypes_controller.php @@ -3,7 +3,7 @@ use Engelsystem\Mail\EngelsystemMailer; use Engelsystem\Models\User\User; use Psr\Log\LoggerInterface; -use Swift_SwiftException as SwiftException; +use Symfony\Component\Mailer\Exception\TransportException; /** * Display a hint for team/angeltype supporters if there are unconfirmed users for his angeltype. @@ -206,7 +206,7 @@ function user_angeltype_confirm_email(User $user, array $angeltype): void 'emails/angeltype-confirmed', ['name' => $angeltype['name'], 'angeltype' => $angeltype, 'username' => $user->name] ); - } catch (SwiftException $e) { + } catch (TransportException $e) { /** @var LoggerInterface $logger */ $logger = app('logger'); $logger->error( @@ -236,7 +236,7 @@ function user_angeltype_add_email(User $user, array $angeltype): void 'emails/angeltype-added', ['name' => $angeltype['name'], 'angeltype' => $angeltype, 'username' => $user->name] ); - } catch (SwiftException $e) { + } catch (TransportException $e) { /** @var LoggerInterface $logger */ $logger = app('logger'); $logger->error( diff --git a/includes/helper/email_helper.php b/includes/helper/email_helper.php index 3012d5ce..b2473e64 100644 --- a/includes/helper/email_helper.php +++ b/includes/helper/email_helper.php @@ -22,19 +22,20 @@ function engelsystem_email_to_user($recipientUser, $title, $message, $notIfItsMe $translator = app()->get('translator'); $locale = $translator->getLocale(); + $status = true; try { /** @var EngelsystemMailer $mailer */ $mailer = app('mailer'); $translator->setLocale($recipientUser->settings->language); - $status = $mailer->sendView( + $mailer->sendView( $recipientUser->contact->email ? $recipientUser->contact->email : $recipientUser->email, $title, 'emails/mail', ['username' => $recipientUser->name, 'message' => $message] ); } catch (Exception $e) { - $status = 0; + $status = false; engelsystem_log(sprintf( 'An exception occurred while sending a mail to %s in %s:%u: %s', $recipientUser->name, @@ -51,5 +52,5 @@ function engelsystem_email_to_user($recipientUser, $title, $message, $notIfItsMe engelsystem_log(sprintf('User %s could not be notified by email due to an error.', $recipientUser->name)); } - return (bool)$status; + return $status; } diff --git a/src/Events/Listener/News.php b/src/Events/Listener/News.php index 4e441dbd..7cb9836d 100644 --- a/src/Events/Listener/News.php +++ b/src/Events/Listener/News.php @@ -8,7 +8,7 @@ use Engelsystem\Models\User\Settings as UserSettings; use Engelsystem\Models\User\User; use Illuminate\Database\Eloquent\Collection; use Psr\Log\LoggerInterface; -use Swift_SwiftException as SwiftException; +use Symfony\Component\Mailer\Exception\TransportException; class News { @@ -67,7 +67,7 @@ class News $template, ['title' => $news->title, 'news' => $news, 'username' => $user->name] ); - } catch (SwiftException $e) { + } catch (TransportException $e) { $this->log->error( 'Unable to send email "{title}" to user {user} with {exception}', ['title' => $subject, 'user' => $user->name, 'exception' => $e] diff --git a/src/Mail/EngelsystemMailer.php b/src/Mail/EngelsystemMailer.php index fe6e9f8b..b5785d02 100644 --- a/src/Mail/EngelsystemMailer.php +++ b/src/Mail/EngelsystemMailer.php @@ -5,7 +5,7 @@ namespace Engelsystem\Mail; use Engelsystem\Helpers\Translation\Translator; use Engelsystem\Models\User\User; use Engelsystem\Renderer\Renderer; -use Swift_Mailer as SwiftMailer; +use Symfony\Component\Mailer\MailerInterface; class EngelsystemMailer extends Mailer { @@ -19,11 +19,11 @@ class EngelsystemMailer extends Mailer protected $subjectPrefix = null; /** - * @param SwiftMailer $mailer - * @param Renderer $view - * @param Translator $translation + * @param MailerInterface $mailer + * @param Renderer|null $view + * @param Translator|null $translation */ - public function __construct(SwiftMailer $mailer, Renderer $view = null, Translator $translation = null) + public function __construct(MailerInterface $mailer, Renderer $view = null, Translator $translation = null) { parent::__construct($mailer); @@ -37,7 +37,6 @@ class EngelsystemMailer extends Mailer * @param string $template * @param array $data * @param string|null $locale - * @return int */ public function sendViewTranslated( $to, @@ -45,7 +44,7 @@ class EngelsystemMailer extends Mailer string $template, array $data = [], ?string $locale = null - ): int { + ): void { if ($to instanceof User) { $locale = $locale ?: $to->settings->language; $to = $to->contact->email ? $to->contact->email : $to->email; @@ -62,13 +61,11 @@ class EngelsystemMailer extends Mailer } $subject = $this->translation ? $this->translation->translate($subject, $data) : $subject; - $sentMails = $this->sendView($to, $subject, $template, $data); + $this->sendView($to, $subject, $template, $data); if ($activeLocale) { $this->translation->setLocale($activeLocale); } - - return $sentMails; } /** @@ -78,13 +75,12 @@ class EngelsystemMailer extends Mailer * @param string $subject * @param string $template * @param array $data - * @return int */ - public function sendView($to, string $subject, string $template, array $data = []): int + public function sendView($to, string $subject, string $template, array $data = []): void { $body = $this->view->render($template, $data); - return $this->send($to, $subject, $body); + $this->send($to, $subject, $body); } /** @@ -93,15 +89,14 @@ class EngelsystemMailer extends Mailer * @param string|string[] $to * @param string $subject * @param string $body - * @return int */ - public function send($to, string $subject, string $body): int + public function send($to, string $subject, string $body): void { if ($this->subjectPrefix) { $subject = sprintf('[%s] %s', $this->subjectPrefix, trim($subject)); } - return parent::send($to, $subject, $body); + parent::send($to, $subject, $body); } /** diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php index 5f0091c1..c1112909 100644 --- a/src/Mail/Mailer.php +++ b/src/Mail/Mailer.php @@ -2,12 +2,12 @@ namespace Engelsystem\Mail; -use Swift_Mailer as SwiftMailer; -use Swift_Message as SwiftMessage; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Email; class Mailer { - /** @var SwiftMailer */ + /** @var MailerInterface */ protected $mailer; /** @var string */ @@ -16,7 +16,7 @@ class Mailer /** @var string */ protected $fromName = null; - public function __construct(SwiftMailer $mailer) + public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } @@ -27,18 +27,16 @@ class Mailer * @param string|string[] $to * @param string $subject * @param string $body - * @return int */ - public function send($to, string $subject, string $body): int + public function send($to, string $subject, string $body): void { - /** @var SwiftMessage $message */ - $message = $this->mailer->createMessage(); - $message->setTo((array)$to) - ->setFrom($this->fromAddress, $this->fromName) - ->setSubject($subject) - ->setBody($body); + $message = (new Email()) + ->to(...(array)$to) + ->from(sprintf('%s <%s>', $this->fromName, $this->fromAddress)) + ->subject($subject) + ->text($body); - return $this->mailer->send($message); + $this->mailer->send($message); } /** diff --git a/src/Mail/MailerServiceProvider.php b/src/Mail/MailerServiceProvider.php index 52c0c1ab..7d80e1a1 100644 --- a/src/Mail/MailerServiceProvider.php +++ b/src/Mail/MailerServiceProvider.php @@ -5,11 +5,13 @@ namespace Engelsystem\Mail; use Engelsystem\Config\Config; use Engelsystem\Container\ServiceProvider; use Engelsystem\Mail\Transport\LogTransport; -use InvalidArgumentException; -use Swift_Mailer as SwiftMailer; -use Swift_SendmailTransport as SendmailTransport; -use Swift_SmtpTransport as SmtpTransport; -use Swift_Transport as Transport; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mailer\Transport; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mailer\Mailer as SymfonyMailer; class MailerServiceProvider extends ServiceProvider { @@ -20,13 +22,14 @@ class MailerServiceProvider extends ServiceProvider $mailConfig = $config->get('email'); $transport = $this->getTransport($mailConfig['driver'], $mailConfig); - $this->app->instance(Transport::class, $transport); + $this->app->instance(TransportInterface::class, $transport); $this->app->instance('mailer.transport', $transport); - /** @var SwiftMailer $swiftMailer */ - $swiftMailer = $this->app->make(SwiftMailer::class); - $this->app->instance(SwiftMailer::class, $swiftMailer); - $this->app->instance('mailer.swift', $swiftMailer); + /** @var SymfonyMailer $symfonyMailer */ + $symfonyMailer = $this->app->make(SymfonyMailer::class); + $this->app->instance(SymfonyMailer::class, $symfonyMailer); + $this->app->instance(MailerInterface::class, $symfonyMailer); + $this->app->instance('mailer.symfony', $symfonyMailer); /** @var EngelsystemMailer $mailer */ $mailer = $this->app->make(EngelsystemMailer::class); @@ -44,7 +47,7 @@ class MailerServiceProvider extends ServiceProvider /** * @param string $transport * @param array $config - * @return Transport + * @return TransportInterface */ protected function getTransport($transport, $config) { @@ -53,12 +56,12 @@ class MailerServiceProvider extends ServiceProvider return $this->app->make(LogTransport::class); case 'mail': case 'sendmail': - return $this->app->make(SendmailTransport::class, ['command' => $config['sendmail']]); + return $this->app->make(SendmailTransport::class, ['command' => $config['sendmail'] ?? null]); case 'smtp': return $this->getSmtpTransport($config); + default: + return Transport::fromDsn($transport ?? ''); } - - throw new InvalidArgumentException(sprintf('Mail driver "%s" not found', $transport)); } /** @@ -67,18 +70,18 @@ class MailerServiceProvider extends ServiceProvider */ protected function getSmtpTransport(array $config) { - /** @var SmtpTransport $transport */ - $transport = $this->app->make(SmtpTransport::class, [ - 'host' => $config['host'], - 'port' => $config['port'], - 'encryption' => $config['encryption'], + /** @var EsmtpTransport $transport */ + $transport = $this->app->make(EsmtpTransport::class, [ + 'host' => $config['host'] ?? 'localhost', + 'port' => $config['port'] ?? 0, + 'tls' => $config['tls'] ?? null, ]); - if ($config['username']) { + if (!empty($config['username'])) { $transport->setUsername($config['username']); } - if ($config['password']) { + if (!empty($config['password'])) { $transport->setPassword($config['password']); } diff --git a/src/Mail/Transport/LogTransport.php b/src/Mail/Transport/LogTransport.php index 632a6bc0..71599fc9 100644 --- a/src/Mail/Transport/LogTransport.php +++ b/src/Mail/Transport/LogTransport.php @@ -3,9 +3,10 @@ namespace Engelsystem\Mail\Transport; use Psr\Log\LoggerInterface; -use Swift_Mime_SimpleMessage as SimpleMessage; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\AbstractTransport; -class LogTransport extends Transport +class LogTransport extends AbstractTransport { /** @var LoggerInterface */ protected $logger; @@ -13,32 +14,37 @@ class LogTransport extends Transport public function __construct(LoggerInterface $logger) { $this->logger = $logger; + + parent::__construct(); } /** - * Send the given Message. + * Send the message to log * - * Recipient/sender data will be retrieved from the Message API. - * The return value is the number of recipients - * - * @param SimpleMessage $message - * @param string[] $failedRecipients An array of failures by-reference - * - * @return int + * @param SentMessage $message */ - public function send( - SimpleMessage $message, - &$failedRecipients = null - ): int { + protected function doSend(SentMessage $message): void + { + $recipients = []; + $messageRecipients = $message->getEnvelope()->getRecipients(); + foreach ($messageRecipients as $recipient) { + $recipients[] = $recipient->toString(); + } + $this->logger->debug( - 'Mail: Send mail "{title}" to "{recipients}":' . PHP_EOL . PHP_EOL . '{content}', + 'Mail: Send mail to "{recipients}":' . PHP_EOL . PHP_EOL . '{content}', [ - 'title' => $message->getSubject(), - 'recipients' => $this->getTo($message), + 'recipients' => implode(', ', $recipients), 'content' => $message->toString(), ] ); + } - return count($this->allRecipients($message)); + /** + * @return string + */ + public function __toString(): string + { + return 'log://'; } } diff --git a/src/Mail/Transport/Transport.php b/src/Mail/Transport/Transport.php deleted file mode 100644 index b83620a6..00000000 --- a/src/Mail/Transport/Transport.php +++ /dev/null @@ -1,109 +0,0 @@ -ping()) { - * $transport->stop(); - * $transport->start(); - * } - * - * The Transport mechanism will be started, if it is not already. - * - * It is undefined if the Transport mechanism attempts to restart as long as - * the return value reflects whether the mechanism is now functional. - * - * @return bool TRUE if the transport is alive - */ - public function ping(): bool - { - return true; - } - - /** - * Register a plugin in the Transport. - * - * @param Swift_Events_EventListener $plugin - */ - public function registerPlugin(Swift_Events_EventListener $plugin) - { - } - - /** - * Returns a unified list of all recipients - * - * @param SimpleMessage $message - * @return array - */ - protected function allRecipients(SimpleMessage $message): array - { - return array_merge( - (array)$message->getTo(), - (array)$message->getCc(), - (array)$message->getBcc() - ); - } - - /** - * Returns a concatenated list of mail recipients - * - * @param SimpleMessage $message - * @return string - */ - protected function getTo(SimpleMessage $message): string - { - return $this->formatTo($this->allRecipients($message)); - } - - /** - * @param array $recipients - * @return string - */ - protected function formatTo(array $recipients) - { - $list = []; - foreach ($recipients as $address => $name) { - $list[] = $name ? sprintf('%s <%s>', $name, $address) : $address; - } - - return implode(',', $list); - } -} diff --git a/tests/Unit/Events/Listener/NewsTest.php b/tests/Unit/Events/Listener/NewsTest.php index 5110f970..fb0360a1 100644 --- a/tests/Unit/Events/Listener/NewsTest.php +++ b/tests/Unit/Events/Listener/NewsTest.php @@ -12,7 +12,7 @@ use Engelsystem\Test\Unit\TestCase; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Psr\Log\Test\TestLogger; -use Swift_SwiftException as SwiftException; +use Symfony\Component\Mailer\Exception\TransportException; class NewsTest extends TestCase { @@ -46,11 +46,9 @@ class NewsTest extends TestCase $this->assertEquals('emails/news-new', $template); $this->assertEquals('Foo', array_values($data)[0]); - if ($i++ > 0) { - throw new SwiftException('Oops'); + if ($i++ > 0) { // On second run + throw new TransportException('Oops'); } - - return 1; }); /** @var News $listener */ diff --git a/tests/Unit/Mail/EngelsystemMailerTest.php b/tests/Unit/Mail/EngelsystemMailerTest.php index 6a3642e5..96f16f51 100644 --- a/tests/Unit/Mail/EngelsystemMailerTest.php +++ b/tests/Unit/Mail/EngelsystemMailerTest.php @@ -11,8 +11,9 @@ use Engelsystem\Renderer\Renderer; use Engelsystem\Test\Unit\HasDatabase; use Engelsystem\Test\Unit\TestCase; use PHPUnit\Framework\MockObject\MockObject; -use Swift_Mailer as SwiftMailer; -use Swift_Message as SwiftMessage; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\RawMessage; class EngelsystemMailerTest extends TestCase { @@ -26,18 +27,17 @@ class EngelsystemMailerTest extends TestCase { /** @var Renderer|MockObject $view */ $view = $this->createMock(Renderer::class); - /** @var SwiftMailer|MockObject $swiftMailer */ - $swiftMailer = $this->createMock(SwiftMailer::class); + /** @var MailerInterface|MockObject $symfonyMailer */ + $symfonyMailer = $this->getMockForAbstractClass(MailerInterface::class); /** @var EngelsystemMailer|MockObject $mailer */ $mailer = $this->getMockBuilder(EngelsystemMailer::class) - ->setConstructorArgs(['mailer' => $swiftMailer, 'view' => $view]) + ->setConstructorArgs(['mailer' => $symfonyMailer, 'view' => $view]) ->onlyMethods(['send']) ->getMock(); - $this->setExpects($mailer, 'send', ['foo@bar.baz', 'Lorem dolor', 'Rendered Stuff!'], 1); + $this->setExpects($mailer, 'send', ['foo@bar.baz', 'Lorem dolor', 'Rendered Stuff!']); $this->setExpects($view, 'render', ['test/template.tpl', ['dev' => true]], 'Rendered Stuff!'); - $return = $mailer->sendView('foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]); - $this->assertEquals(1, $return); + $mailer->sendView('foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]); } /** @@ -54,18 +54,18 @@ class EngelsystemMailerTest extends TestCase /** @var Renderer|MockObject $view */ $view = $this->createMock(Renderer::class); - /** @var SwiftMailer|MockObject $swiftMailer */ - $swiftMailer = $this->createMock(SwiftMailer::class); + /** @var MailerInterface|MockObject $symfonyMailer */ + $symfonyMailer = $this->createMock(MailerInterface::class); /** @var Translator|MockObject $translator */ $translator = $this->createMock(Translator::class); /** @var EngelsystemMailer|MockObject $mailer */ $mailer = $this->getMockBuilder(EngelsystemMailer::class) - ->setConstructorArgs(['mailer' => $swiftMailer, 'view' => $view, 'translation' => $translator]) + ->setConstructorArgs(['mailer' => $symfonyMailer, 'view' => $view, 'translation' => $translator]) ->onlyMethods(['sendView']) ->getMock(); - $this->setExpects($mailer, 'sendView', ['foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]], 1); + $this->setExpects($mailer, 'sendView', ['foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]]); $this->setExpects($translator, 'getLocales', null, ['de_DE' => 'de_DE', 'en_US' => 'en_US']); $this->setExpects($translator, 'getLocale', null, 'en_US'); $this->setExpects($translator, 'translate', ['translatable.text', ['dev' => true]], 'Lorem dolor'); @@ -73,14 +73,13 @@ class EngelsystemMailerTest extends TestCase ->method('setLocale') ->withConsecutive(['de_DE'], ['en_US']); - $return = $mailer->sendViewTranslated( + $mailer->sendViewTranslated( $user, 'translatable.text', 'test/template.tpl', ['dev' => true], 'de_DE' ); - $this->assertEquals(1, $return); } /** @@ -90,25 +89,26 @@ class EngelsystemMailerTest extends TestCase */ public function testSend() { - /** @var SwiftMessage|MockObject $message */ - $message = $this->createMock(SwiftMessage::class); - /** @var SwiftMailer|MockObject $swiftMailer */ - $swiftMailer = $this->createMock(SwiftMailer::class); - $this->setExpects($swiftMailer, 'createMessage', null, $message); - $this->setExpects($swiftMailer, 'send', null, 1); - $this->setExpects($message, 'setTo', [['to@xam.pel']], $message); - $this->setExpects($message, 'setFrom', ['foo@bar.baz', 'Lorem Ipsum'], $message); - $this->setExpects($message, 'setSubject', ['[Mail test] Foo Bar'], $message); - $this->setExpects($message, 'setBody', ['Lorem Ipsum!'], $message); + /** @var MailerInterface|MockObject $symfonyMailer */ + $symfonyMailer = $this->createMock(MailerInterface::class); - $mailer = new EngelsystemMailer($swiftMailer); + $symfonyMailer->expects($this->once()) + ->method('send') + ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null) { + $this->assertStringContainsString('foo@bar.baz', $message->toString()); + $this->assertStringContainsString('Foo Bar', $message->toString()); + $this->assertStringContainsString('Mail test', $message->toString()); + $this->assertStringContainsString('to@xam.pel', $message->toString()); + $this->assertStringContainsString('Lorem Ipsum!', $message->toString()); + }); + + $mailer = new EngelsystemMailer($symfonyMailer); $mailer->setFromAddress('foo@bar.baz'); - $mailer->setFromName('Lorem Ipsum'); + $mailer->setFromName('Foo Bar'); $mailer->setSubjectPrefix('Mail test'); $this->assertEquals('Mail test', $mailer->getSubjectPrefix()); - $return = $mailer->send('to@xam.pel', 'Foo Bar ', 'Lorem Ipsum!'); - $this->assertEquals(1, $return); + $mailer->send('to@xam.pel', 'Foo Bar ', 'Lorem Ipsum!'); } } diff --git a/tests/Unit/Mail/MailerServiceProviderTest.php b/tests/Unit/Mail/MailerServiceProviderTest.php index 6214491b..46585947 100644 --- a/tests/Unit/Mail/MailerServiceProviderTest.php +++ b/tests/Unit/Mail/MailerServiceProviderTest.php @@ -11,10 +11,10 @@ use Engelsystem\Mail\Transport\LogTransport; use Engelsystem\Test\Unit\ServiceProviderTest; use InvalidArgumentException; use Psr\Log\LoggerInterface; -use Swift_Mailer as SwiftMailer; -use Swift_SendmailTransport as SendmailTransport; -use Swift_SmtpTransport as SmtpTransport; -use Swift_Transport as Transport; +use Symfony\Component\Mailer\Mailer as SymfonyMailer; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mailer\Transport\TransportInterface; class MailerServiceProviderTest extends ServiceProviderTest { @@ -37,7 +37,7 @@ class MailerServiceProviderTest extends ServiceProviderTest 'driver' => 'smtp', 'host' => 'mail.foo.bar', 'port' => 587, - 'encryption' => 'tls', + 'tls' => true, 'username' => 'foobar', 'password' => 'LoremIpsum123', ], @@ -53,8 +53,8 @@ class MailerServiceProviderTest extends ServiceProviderTest $serviceProvider = new MailerServiceProvider($app); $serviceProvider->register(); - $this->assertExistsInContainer(['mailer.transport', Transport::class], $app); - $this->assertExistsInContainer(['mailer.swift', SwiftMailer::class], $app); + $this->assertExistsInContainer(['mailer.transport', TransportInterface::class], $app); + $this->assertExistsInContainer(['mailer.symfony', SymfonyMailer::class], $app); $this->assertExistsInContainer(['mailer', EngelsystemMailer::class, Mailer::class], $app); /** @var EngelsystemMailer $mailer */ @@ -65,7 +65,7 @@ class MailerServiceProviderTest extends ServiceProviderTest /** @var SendmailTransport $transport */ $transport = $app->get('mailer.transport'); - $this->assertEquals($this->defaultConfig['email']['sendmail'], $transport->getCommand()); + $this->assertInstanceOf(SendmailTransport::class, $transport); } /** @@ -78,7 +78,7 @@ class MailerServiceProviderTest extends ServiceProviderTest [SendmailTransport::class, ['email' => ['driver' => 'mail']]], [SendmailTransport::class, ['email' => ['driver' => 'sendmail']]], [ - SmtpTransport::class, + EsmtpTransport::class, $this->smtpConfig, ], ]; @@ -123,12 +123,9 @@ class MailerServiceProviderTest extends ServiceProviderTest $serviceProvider = new MailerServiceProvider($app); $serviceProvider->register(); - /** @var SmtpTransport $transport */ + /** @var EsmtpTransport $transport */ $transport = $app->get('mailer.transport'); - $this->assertEquals($this->smtpConfig['email']['host'], $transport->getHost()); - $this->assertEquals($this->smtpConfig['email']['port'], $transport->getPort()); - $this->assertEquals($this->smtpConfig['email']['encryption'], $transport->getEncryption()); $this->assertEquals($this->smtpConfig['email']['username'], $transport->getUsername()); $this->assertEquals($this->smtpConfig['email']['password'], $transport->getPassword()); } diff --git a/tests/Unit/Mail/MailerTest.php b/tests/Unit/Mail/MailerTest.php index a0edc11a..e3bde7af 100644 --- a/tests/Unit/Mail/MailerTest.php +++ b/tests/Unit/Mail/MailerTest.php @@ -5,8 +5,9 @@ namespace Engelsystem\Test\Unit\Mail; use Engelsystem\Mail\Mailer; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Swift_Mailer as SwiftMailer; -use Swift_Message as SwiftMessage; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\RawMessage; class MailerTest extends TestCase { @@ -19,10 +20,10 @@ class MailerTest extends TestCase */ public function testInitAndSettersAndGetters() { - /** @var SwiftMailer|MockObject $swiftMailer */ - $swiftMailer = $this->createMock(SwiftMailer::class); + /** @var MailerInterface|MockObject $symfonyMailer */ + $symfonyMailer = $this->createMock(MailerInterface::class); - $mailer = new Mailer($swiftMailer); + $mailer = new Mailer($symfonyMailer); $mailer->setFromName('From Name'); $this->assertEquals('From Name', $mailer->getFromName()); @@ -36,42 +37,22 @@ class MailerTest extends TestCase */ public function testSend() { - /** @var SwiftMessage|MockObject $message */ - $message = $this->createMock(SwiftMessage::class); - /** @var SwiftMailer|MockObject $swiftMailer */ - $swiftMailer = $this->createMock(SwiftMailer::class); - $swiftMailer->expects($this->once()) - ->method('createMessage') - ->willReturn($message); - $swiftMailer->expects($this->once()) + /** @var MailerInterface|MockObject $symfonyMailer */ + $symfonyMailer = $this->createMock(MailerInterface::class); + $symfonyMailer->expects($this->once()) ->method('send') - ->willReturn(1); + ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null) { + $this->assertStringContainsString('to@xam.pel', $message->toString()); + $this->assertStringContainsString('foo@bar.baz', $message->toString()); + $this->assertStringContainsString('Test Tester', $message->toString()); + $this->assertStringContainsString('Foo Bar', $message->toString()); + $this->assertStringContainsString('Lorem Ipsum!', $message->toString()); + }); - $message->expects($this->once()) - ->method('setTo') - ->with(['to@xam.pel']) - ->willReturn($message); - - $message->expects($this->once()) - ->method('setFrom') - ->with('foo@bar.baz', 'Lorem Ipsum') - ->willReturn($message); - - $message->expects($this->once()) - ->method('setSubject') - ->with('Foo Bar') - ->willReturn($message); - - $message->expects($this->once()) - ->method('setBody') - ->with('Lorem Ipsum!') - ->willReturn($message); - - $mailer = new Mailer($swiftMailer); + $mailer = new Mailer($symfonyMailer); $mailer->setFromAddress('foo@bar.baz'); - $mailer->setFromName('Lorem Ipsum'); + $mailer->setFromName('Test Tester'); - $return = $mailer->send('to@xam.pel', 'Foo Bar', 'Lorem Ipsum!'); - $this->assertEquals(1, $return); + $mailer->send('to@xam.pel', 'Foo Bar', 'Lorem Ipsum!'); } } diff --git a/tests/Unit/Mail/Transport/LogTransportTest.php b/tests/Unit/Mail/Transport/LogTransportTest.php index a6d64931..eb5f0b6e 100644 --- a/tests/Unit/Mail/Transport/LogTransportTest.php +++ b/tests/Unit/Mail/Transport/LogTransportTest.php @@ -6,52 +6,39 @@ use Engelsystem\Mail\Transport\LogTransport; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; -use Swift_Mime_SimpleMessage as SimpleMessage; +use Psr\Log\Test\TestLogger; +use Symfony\Component\Mime\Email; class LogTransportTest extends TestCase { /** * @covers \Engelsystem\Mail\Transport\LogTransport::__construct - * @covers \Engelsystem\Mail\Transport\LogTransport::send + * @covers \Engelsystem\Mail\Transport\LogTransport::doSend */ public function testSend() + { + $logger = new TestLogger(); + $email = (new Email()) + ->from('some@email.host') + ->to('foo@bar.baz', 'Test Tester ') + ->subject('Testing') + ->text('Message body'); + + $transport = new LogTransport($logger); + $transport->send($email); + + $this->assertTrue($logger->hasDebugThatContains('Send mail to')); + } + + /** + * @covers \Engelsystem\Mail\Transport\LogTransport::__toString + */ + public function testToString() { /** @var LoggerInterface|MockObject $logger */ $logger = $this->getMockForAbstractClass(LoggerInterface::class); - /** @var SimpleMessage|MockObject $message */ - $message = $this->createMock(SimpleMessage::class); - $message->expects($this->once()) - ->method('getSubject') - ->willReturn('Some subject'); - $message->expects($this->once()) - ->method('toString') - ->willReturn("Head: er\n\nMessage body"); - - $logger->expects($this->once()) - ->method('debug') - ->willReturnCallback(function ($message, $context = []) { - foreach (array_keys($context) as $key) { - $this->assertStringContainsString(sprintf('{%s}', $key), $message); - } - - $this->assertEquals('Some subject', $context['title']); - $this->assertEquals('foo@bar.batz,Lorem Ipsum ', $context['recipients']); - $this->assertStringContainsString('Head: er', $context['content']); - $this->assertStringContainsString('Message body', $context['content']); - }); - - /** @var LogTransport|MockObject $transport */ - $transport = $this->getMockBuilder(LogTransport::class) - ->setConstructorArgs(['logger' => $logger]) - ->onlyMethods(['allRecipients']) - ->getMock(); - $transport->expects($this->exactly(2)) - ->method('allRecipients') - ->with($message) - ->willReturn(['foo@bar.batz' => null, 'lor@em.ips' => 'Lorem Ipsum']); - - $return = $transport->send($message); - $this->assertEquals(2, $return); + $transport = new LogTransport($logger); + $this->assertEquals('log://', (string)$transport); } } diff --git a/tests/Unit/Mail/Transport/Stub/TransportImplementation.php b/tests/Unit/Mail/Transport/Stub/TransportImplementation.php deleted file mode 100644 index e3667c6e..00000000 --- a/tests/Unit/Mail/Transport/Stub/TransportImplementation.php +++ /dev/null @@ -1,35 +0,0 @@ -allRecipients($message); - } - - /** - * @param SimpleMessage $message - * @return string - */ - public function getGetTo(SimpleMessage $message) - { - return $this->getTo($message); - } -} diff --git a/tests/Unit/Mail/Transport/TransportTest.php b/tests/Unit/Mail/Transport/TransportTest.php deleted file mode 100644 index 8dd78d78..00000000 --- a/tests/Unit/Mail/Transport/TransportTest.php +++ /dev/null @@ -1,95 +0,0 @@ -getMockForAbstractClass(Swift_Events_EventListener::class); - - $transport = new TransportImplementation(); - - $transport->start(); - $transport->registerPlugin($plugin); - - $this->assertTrue($transport->isStarted()); - $this->assertTrue($transport->ping()); - - $transport->stop(); - } - - /** - * @covers \Engelsystem\Mail\Transport\Transport::allRecipients - */ - public function testAllRecipients() - { - /** @var SimpleMessage|MockObject $message */ - $message = $this->createMock(SimpleMessage::class); - $transport = new TransportImplementation(); - $message->expects($this->once()) - ->method('getTo') - ->willReturn([ - 'foo@bar.batz' => 'Foo Bar', - 'lorem@ipsum.dolor' => null, - ]); - $message->expects($this->once()) - ->method('getCc') - ->willReturn([ - 'to@bar.batz' => null, - ]); - $message->expects($this->once()) - ->method('getBcc') - ->willReturn([ - 'secret@bar.batz' => 'I\'m secret!', - ]); - - $this->assertEquals( - [ - 'foo@bar.batz' => 'Foo Bar', - 'lorem@ipsum.dolor' => null, - 'to@bar.batz' => null, - 'secret@bar.batz' => 'I\'m secret!', - ], - $transport->getAllRecipients($message) - ); - } - - /** - * @covers \Engelsystem\Mail\Transport\Transport::formatTo - * @covers \Engelsystem\Mail\Transport\Transport::getTo - */ - public function testGetTo() - { - /** @var SimpleMessage|MockObject $message */ - $message = $this->createMock(SimpleMessage::class); - /** @var TransportImplementation|MockObject $transport */ - $transport = $this->getMockBuilder(TransportImplementation::class) - ->onlyMethods(['allRecipients']) - ->getMock(); - $transport->expects($this->once()) - ->method('allRecipients') - ->with($message) - ->willReturn([ - 'foo@bar.batz' => null, - 'lorem@ipsum.dolor' => 'Developer', - ]); - - $return = $transport->getGetTo($message); - $this->assertEquals('foo@bar.batz,Developer ', $return); - } -}