diff --git a/composer.json b/composer.json index e8c8079d535f..716c37d35290 100644 --- a/composer.json +++ b/composer.json @@ -29,12 +29,12 @@ "psr/container": "^1.1.1|^2.0.1", "psr/simple-cache": "^1.0", "ramsey/uuid": "^4.0", - "swiftmailer/swiftmailer": "^6.2.7", "symfony/console": "^6.0", "symfony/error-handler": "^6.0", "symfony/finder": "^6.0", "symfony/http-foundation": "^6.0", "symfony/http-kernel": "^6.0", + "symfony/mailer": "^6.0", "symfony/mime": "^6.0", "symfony/process": "^6.0", "symfony/routing": "^6.0", @@ -136,12 +136,12 @@ "ext-pcntl": "Required to use all features of the queue worker.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.189.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver and DynamoDb failed job storage (^3.189.0).", "brianium/paratest": "Required to run tests in parallel (^6.0).", "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.12|^3.0).", "filp/whoops": "Required for friendly error pages in development (^2.8).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", - "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^7.2).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.2).", "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^2.0).", "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^2.0).", @@ -153,10 +153,13 @@ "predis/predis": "Required to use the predis connector (^1.1.2).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^5.0|^6.0).", + "symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).", "symfony/cache": "Required to PSR-6 cache bridge (^6.0).", "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Contracts/Mail/Mailer.php b/src/Illuminate/Contracts/Mail/Mailer.php index 255b6789da82..38f9e3b56cc1 100644 --- a/src/Illuminate/Contracts/Mail/Mailer.php +++ b/src/Illuminate/Contracts/Mail/Mailer.php @@ -38,11 +38,4 @@ public function raw($text, $callback); * @return void */ public function send($view, array $data = [], $callback = null); - - /** - * Get the array of failed recipients. - * - * @return array - */ - public function failures(); } diff --git a/src/Illuminate/Mail/Events/MessageSending.php b/src/Illuminate/Mail/Events/MessageSending.php index bf5bccfdf2df..31435fb6e0e7 100644 --- a/src/Illuminate/Mail/Events/MessageSending.php +++ b/src/Illuminate/Mail/Events/MessageSending.php @@ -2,12 +2,14 @@ namespace Illuminate\Mail\Events; +use Symfony\Component\Mime\Email; + class MessageSending { /** - * The Swift message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ public $message; @@ -21,11 +23,11 @@ class MessageSending /** * Create a new event instance. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return void */ - public function __construct($message, $data = []) + public function __construct(Email $message, array $data = []) { $this->data = $data; $this->message = $message; diff --git a/src/Illuminate/Mail/Events/MessageSent.php b/src/Illuminate/Mail/Events/MessageSent.php index 64aef94312b6..a95148fcc285 100644 --- a/src/Illuminate/Mail/Events/MessageSent.php +++ b/src/Illuminate/Mail/Events/MessageSent.php @@ -2,14 +2,14 @@ namespace Illuminate\Mail\Events; -use Swift_Attachment; +use Symfony\Component\Mime\Email; class MessageSent { /** - * The Swift message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ public $message; @@ -23,11 +23,11 @@ class MessageSent /** * Create a new event instance. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return void */ - public function __construct($message, $data = []) + public function __construct(Email $message, array $data = []) { $this->data = $data; $this->message = $message; @@ -40,9 +40,7 @@ public function __construct($message, $data = []) */ public function __serialize() { - $hasAttachments = collect($this->message->getChildren()) - ->whereInstanceOf(Swift_Attachment::class) - ->isNotEmpty(); + $hasAttachments = collect($this->message->getAttachments())->isNotEmpty(); return $hasAttachments ? [ 'message' => base64_encode(serialize($this->message)), diff --git a/src/Illuminate/Mail/MailManager.php b/src/Illuminate/Mail/MailManager.php index aef832ccef23..5b546738365d 100644 --- a/src/Illuminate/Mail/MailManager.php +++ b/src/Illuminate/Mail/MailManager.php @@ -2,26 +2,24 @@ namespace Illuminate\Mail; -use Aws\SesV2\SesV2Client; use Closure; -use GuzzleHttp\Client as HttpClient; use Illuminate\Contracts\Mail\Factory as FactoryContract; use Illuminate\Log\LogManager; use Illuminate\Mail\Transport\ArrayTransport; use Illuminate\Mail\Transport\LogTransport; -use Illuminate\Mail\Transport\MailgunTransport; -use Illuminate\Mail\Transport\SesTransport; use Illuminate\Support\Arr; use Illuminate\Support\Str; use InvalidArgumentException; -use Postmark\ThrowExceptionOnFailurePlugin; -use Postmark\Transport as PostmarkTransport; use Psr\Log\LoggerInterface; -use Swift_DependencyContainer; -use Swift_FailoverTransport as FailoverTransport; -use Swift_Mailer; -use Swift_SendmailTransport as SendmailTransport; -use Swift_SmtpTransport as SmtpTransport; +use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory; +use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; +use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Transport\Dsn; +use Symfony\Component\Mailer\Transport\FailoverTransport; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory; +use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; /** * @mixin \Illuminate\Mail\Mailer @@ -117,7 +115,7 @@ protected function resolve($name) $mailer = new Mailer( $name, $this->app['view'], - $this->createSwiftMailer($config), + $this->createSymfonyTransport($config), $this->app['events'] ); @@ -135,32 +133,15 @@ protected function resolve($name) return $mailer; } - /** - * Create the SwiftMailer instance for the given configuration. - * - * @param array $config - * @return \Swift_Mailer - */ - protected function createSwiftMailer(array $config) - { - if ($config['domain'] ?? false) { - Swift_DependencyContainer::getInstance() - ->register('mime.idgenerator.idright') - ->asValue($config['domain']); - } - - return new Swift_Mailer($this->createTransport($config)); - } - /** * Create a new transport instance. * * @param array $config - * @return \Swift_Transport + * @return \Symfony\Component\Mailer\Transport\TransportInterface * * @throws \InvalidArgumentException */ - public function createTransport(array $config) + public function createSymfonyTransport(array $config) { // Here we will check if the "transport" key exists and if it doesn't we will // assume an application is still using the legacy mail configuration file @@ -179,33 +160,23 @@ public function createTransport(array $config) } /** - * Create an instance of the SMTP Swift Transport driver. + * Create an instance of the Symfony SMTP Transport driver. * * @param array $config - * @return \Swift_SmtpTransport + * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport */ protected function createSmtpTransport(array $config) { - // The Swift SMTP transport instance will allow us to use any SMTP backend - // for delivering mail such as Sendgrid, Amazon SES, or a custom server - // a developer has available. We will just pass this configured host. - $transport = new SmtpTransport( - $config['host'], - $config['port'] - ); - - if (! empty($config['encryption'])) { - $transport->setEncryption($config['encryption']); - } + $factory = new EsmtpTransportFactory; - // Once we have the transport we will check for the presence of a username - // and password. If we have it we will set the credentials on the Swift - // transporter instance so that we'll properly authenticate delivery. - if (isset($config['username'])) { - $transport->setUsername($config['username']); - - $transport->setPassword($config['password']); - } + $transport = $factory->create(new Dsn( + ! empty($config['encryption']) && $config['encryption'] === 'tls' ? 'smtps' : '', + $config['host'], + $config['username'] ?? null, + $config['password'] ?? null, + $config['port'] ?? null, + $config + )); return $this->configureSmtpTransport($transport, $config); } @@ -213,40 +184,32 @@ protected function createSmtpTransport(array $config) /** * Configure the additional SMTP driver options. * - * @param \Swift_SmtpTransport $transport + * @param \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport $transport * @param array $config - * @return \Swift_SmtpTransport + * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport */ - protected function configureSmtpTransport($transport, array $config) + protected function configureSmtpTransport(EsmtpTransport $transport, array $config) { - if (isset($config['stream'])) { - $transport->setStreamOptions($config['stream']); - } + $stream = $transport->getStream(); - if (isset($config['source_ip'])) { - $transport->setSourceIp($config['source_ip']); - } - - if (isset($config['local_domain'])) { - $transport->setLocalDomain($config['local_domain']); - } - - if (isset($config['timeout'])) { - $transport->setTimeout($config['timeout']); - } + if ($stream instanceof SocketStream) { + if (isset($config['source_ip'])) { + $stream->setSourceIp($config['source_ip']); + } - if (isset($config['auth_mode'])) { - $transport->setAuthMode($config['auth_mode']); + if (isset($config['timeout'])) { + $stream->setTimeout($config['timeout']); + } } return $transport; } /** - * Create an instance of the Sendmail Swift Transport driver. + * Create an instance of the Symfony Sendmail Transport driver. * * @param array $config - * @return \Swift_SendmailTransport + * @return \Symfony\Component\Mailer\Transport\SendmailTransport */ protected function createSendmailTransport(array $config) { @@ -256,10 +219,10 @@ protected function createSendmailTransport(array $config) } /** - * Create an instance of the Amazon SES Swift Transport driver. + * Create an instance of the Symfony Amazon SES Transport driver. * * @param array $config - * @return \Illuminate\Mail\Transport\SesTransport + * @return \Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiAsyncAwsTransport */ protected function createSesTransport(array $config) { @@ -271,31 +234,26 @@ protected function createSesTransport(array $config) $config = Arr::except($config, ['transport']); - return new SesTransport( - new SesV2Client($this->addSesCredentials($config)), - $config['options'] ?? [] - ); - } + $factory = new SesTransportFactory(); - /** - * Add the SES credentials to the configuration array. - * - * @param array $config - * @return array - */ - protected function addSesCredentials(array $config) - { - if (! empty($config['key']) && ! empty($config['secret'])) { - $config['credentials'] = Arr::only($config, ['key', 'secret', 'token']); + if (! isset($config['session_token']) && isset($config['token'])) { + $config['session_token'] = $config['token']; } - return $config; + return $factory->create(new Dsn( + 'ses+api', + 'default', + $config['key'], + $config['secret'], + $config['port'] ?? null, + $config + )); } /** - * Create an instance of the Mail Swift Transport driver. + * Create an instance of the Symfony Mail Transport driver. * - * @return \Swift_SendmailTransport + * @return \Symfony\Component\Mailer\Transport\SendmailTransport */ protected function createMailTransport() { @@ -303,50 +261,56 @@ protected function createMailTransport() } /** - * Create an instance of the Mailgun Swift Transport driver. + * Create an instance of the Symfony Mailgun Transport driver. * * @param array $config - * @return \Illuminate\Mail\Transport\MailgunTransport + * @return \Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunApiTransport */ protected function createMailgunTransport(array $config) { + $factory = new MailgunTransportFactory(); + if (! isset($config['secret'])) { $config = $this->app['config']->get('services.mailgun', []); } - return new MailgunTransport( - $this->guzzle($config), + return $factory->create(new Dsn( + 'mailgun+api', + $config['endpoint'] ?? 'default', $config['secret'], - $config['domain'], - $config['endpoint'] ?? null - ); + $config['domain'] + )); } /** - * Create an instance of the Postmark Swift Transport driver. + * Create an instance of the Symfony Postmark Transport driver. * * @param array $config - * @return \Swift_Transport + * @return \Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkApiTransport */ protected function createPostmarkTransport(array $config) { - $headers = isset($config['message_stream_id']) ? [ - 'X-PM-Message-Stream' => $config['message_stream_id'], - ] : []; + $factory = new PostmarkTransportFactory(); + + $options = isset($config['message_stream_id']) + ? ['message_stream' => $config['message_stream_id']] + : []; - return tap(new PostmarkTransport( + return $factory->create(new Dsn( + 'postmark+api', + 'default', $config['token'] ?? $this->app['config']->get('services.postmark.token'), - $headers - ), function ($transport) { - $transport->registerPlugin(new ThrowExceptionOnFailurePlugin); - }); + null, + null, + $options + )); } /** - * Create an instance of the Failover Swift Transport driver. + * Create an instance of the Symfony Failover Transport driver. * * @param array $config - * @return \Swift_FailoverTransport + * @return \Symfony\Component\Mailer\Transport\FailoverTransport */ protected function createFailoverTransport(array $config) { @@ -363,15 +327,15 @@ protected function createFailoverTransport(array $config) // the transport configuration parameter in order to offer compatibility // with any Laravel <= 6.x application style mail configuration files. $transports[] = $this->app['config']['mail.driver'] - ? $this->createTransport(array_merge($config, ['transport' => $name])) - : $this->createTransport($config); + ? $this->createSymfonyTransport(array_merge($config, ['transport' => $name])) + : $this->createSymfonyTransport($config); } return new FailoverTransport($transports); } /** - * Create an instance of the Log Swift Transport driver. + * Create an instance of the Log Transport driver. * * @param array $config * @return \Illuminate\Mail\Transport\LogTransport @@ -390,7 +354,7 @@ protected function createLogTransport(array $config) } /** - * Create an instance of the Array Swift Transport Driver. + * Create an instance of the Array Transport Driver. * * @return \Illuminate\Mail\Transport\ArrayTransport */ @@ -399,21 +363,6 @@ protected function createArrayTransport() return new ArrayTransport; } - /** - * Get a fresh Guzzle HTTP client instance. - * - * @param array $config - * @return \GuzzleHttp\Client - */ - protected function guzzle(array $config) - { - return new HttpClient(Arr::add( - $config['guzzle'] ?? [], - 'connect_timeout', - 60 - )); - } - /** * Set a global address on the mailer by type. * diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index 30cf75030aec..b37d47531559 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -167,11 +167,11 @@ class Mailable implements MailableContract, Renderable * Send the message using the given mailer. * * @param \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer $mailer - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function send($mailer) { - $this->withLocale($this->locale, function () use ($mailer) { + return $this->withLocale($this->locale, function () use ($mailer) { Container::getInstance()->call([$this, 'build']); $mailer = $mailer instanceof MailFactory @@ -450,7 +450,7 @@ protected function buildDiskAttachments($message) protected function runCallbacks($message) { foreach ($this->callbacks as $callback) { - $callback($message->getSwiftMessage()); + $callback($message->getSymfonyMessage()); } return $this; @@ -972,12 +972,12 @@ public function mailer($mailer) } /** - * Register a callback to be called with the Swift message instance. + * Register a callback to be called with the Symfony message instance. * * @param callable $callback * @return $this */ - public function withSwiftMessage($callback) + public function withSymfonyMessage($callback) { $this->callbacks[] = $callback; diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index 128f211f7651..7ca202c3ae79 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -15,7 +15,9 @@ use Illuminate\Support\HtmlString; use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; -use Swift_Mailer; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; class Mailer implements MailerContract, MailQueueContract { @@ -36,11 +38,11 @@ class Mailer implements MailerContract, MailQueueContract protected $views; /** - * The Swift Mailer instance. + * The Symfony Transport instance. * - * @var \Swift_Mailer + * @var \Symfony\Component\Mailer\Transport\TransportInterface */ - protected $swift; + protected $transport; /** * The event dispatcher instance. @@ -84,28 +86,21 @@ class Mailer implements MailerContract, MailQueueContract */ protected $queue; - /** - * Array of failed recipients. - * - * @var array - */ - protected $failedRecipients = []; - /** * Create a new Mailer instance. * * @param string $name * @param \Illuminate\Contracts\View\Factory $views - * @param \Swift_Mailer $swift + * @param \Symfony\Component\Mailer\Transport\TransportInterface $transport * @param \Illuminate\Contracts\Events\Dispatcher|null $events * @return void */ - public function __construct(string $name, Factory $views, Swift_Mailer $swift, Dispatcher $events = null) + public function __construct(string $name, Factory $views, TransportInterface $transport, Dispatcher $events = null) { $this->name = $name; $this->views = $views; - $this->swift = $swift; $this->events = $events; + $this->transport = $transport; } /** @@ -193,11 +188,11 @@ public function bcc($users) * * @param string $html * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function html($html, $callback) { - $this->send(['html' => new HtmlString($html)], [], $callback); + return $this->send(['html' => new HtmlString($html)], [], $callback); } /** @@ -205,11 +200,11 @@ public function html($html, $callback) * * @param string $text * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function raw($text, $callback) { - $this->send(['raw' => $text], [], $callback); + return $this->send(['raw' => $text], [], $callback); } /** @@ -218,11 +213,11 @@ public function raw($text, $callback) * @param string $view * @param array $data * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function plain($view, array $data, $callback) { - $this->send(['text' => $view], $data, $callback); + return $this->send(['text' => $view], $data, $callback); } /** @@ -250,7 +245,7 @@ public function render($view, array $data = []) * @param \Illuminate\Contracts\Mail\Mailable|string|array $view * @param array $data * @param \Closure|string|null $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function send($view, array $data = [], $callback = null) { @@ -268,7 +263,9 @@ public function send($view, array $data = [], $callback = null) // Once we have retrieved the view content for the e-mail we will set the body // of this message using the HTML type, which will provide a simple wrapper // to creating view based emails that are able to receive arrays of data. - $callback($message); + if (! is_null($callback)) { + $callback($message); + } $this->addContent($message, $view, $plain, $raw, $data); @@ -282,12 +279,14 @@ public function send($view, array $data = [], $callback = null) // Next we will determine if the message should be sent. We give the developer // one final chance to stop this message and then we will send it to all of // its recipients. We will then fire the sent event for the sent message. - $swiftMessage = $message->getSwiftMessage(); + $symfonyMessage = $message->getSymfonyMessage(); - if ($this->shouldSendMessage($swiftMessage, $data)) { - $this->sendSwiftMessage($swiftMessage); + if ($this->shouldSendMessage($symfonyMessage, $data)) { + $sentMessage = $this->sendSymfonyMessage($symfonyMessage); $this->dispatchSentEvent($message, $data); + + return $sentMessage === null ? null : new SentMessage($sentMessage); } } @@ -295,7 +294,7 @@ public function send($view, array $data = [], $callback = null) * Send the given mailable. * * @param \Illuminate\Contracts\Mail\Mailable $mailable - * @return mixed + * @return \Illuminate\Mail\SentMessage|null */ protected function sendMailable(MailableContract $mailable) { @@ -352,19 +351,15 @@ protected function parseView($view) protected function addContent($message, $view, $plain, $raw, $data) { if (isset($view)) { - $message->setBody($this->renderView($view, $data) ?: ' ', 'text/html'); + $message->html($this->renderView($view, $data) ?: ' '); } if (isset($plain)) { - $method = isset($view) ? 'addPart' : 'setBody'; - - $message->$method($this->renderView($plain, $data) ?: ' ', 'text/plain'); + $message->text($this->renderView($plain, $data) ?: ' '); } if (isset($raw)) { - $method = (isset($view) || isset($plain)) ? 'addPart' : 'setBody'; - - $message->$method($raw, 'text/plain'); + $message->text($raw); } } @@ -484,7 +479,7 @@ public function laterOn($queue, $delay, $view) */ protected function createMessage() { - $message = new Message($this->swift->createMessage('message')); + $message = new Message(new Email()); // If a global from address has been specified we will set it on every message // instance so the developer does not have to repeat themselves every time @@ -508,26 +503,24 @@ protected function createMessage() } /** - * Send a Swift Message instance. + * Send a Symfony Email instance. * - * @param \Swift_Message $message - * @return int|null + * @param \Symfony\Component\Mime\Email $message + * @return \Symfony\Component\Mailer\SentMessage|null */ - protected function sendSwiftMessage($message) + protected function sendSymfonyMessage(Email $message) { - $this->failedRecipients = []; - try { - return $this->swift->send($message, $this->failedRecipients); + return $this->transport->send($message, Envelope::create($message)); } finally { - $this->forceReconnection(); + // } } /** - * Determines if the message can be sent. + * Determines if the email can be sent. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return bool */ @@ -553,41 +546,19 @@ protected function dispatchSentEvent($message, $data = []) { if ($this->events) { $this->events->dispatch( - new MessageSent($message->getSwiftMessage(), $data) + new MessageSent($message->getSymfonyMessage(), $data) ); } } /** - * Force the transport to re-connect. - * - * This will prevent errors in daemon queue situations. - * - * @return void - */ - protected function forceReconnection() - { - $this->getSwiftMailer()->getTransport()->stop(); - } - - /** - * Get the array of failed recipients. - * - * @return array - */ - public function failures() - { - return $this->failedRecipients; - } - - /** - * Get the Swift Mailer instance. + * Get the Symfony Transport instance. * - * @return \Swift_Mailer + * @return \Symfony\Component\Mailer\Transport\TransportInterface */ - public function getSwiftMailer() + public function getSymfonyTransport() { - return $this->swift; + return $this->transport; } /** @@ -601,14 +572,14 @@ public function getViewFactory() } /** - * Set the Swift Mailer instance. + * Set the Symfony Transport instance. * - * @param \Swift_Mailer $swift + * @param \Symfony\Component\Mailer\Transport\TransportInterface $transport * @return void */ - public function setSwiftMailer($swift) + public function setSymfonyTransport(TransportInterface $transport) { - $this->swift = $swift; + $this->transport = $transport; } /** diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index cab6c026d9fe..e225da983963 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -2,23 +2,24 @@ namespace Illuminate\Mail; +use Illuminate\Support\Str; use Illuminate\Support\Traits\ForwardsCalls; -use Swift_Attachment; -use Swift_Image; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; /** - * @mixin \Swift_Message + * @mixin \Symfony\Component\Mime\Email */ class Message { use ForwardsCalls; /** - * The Swift Message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ - protected $swift; + protected $message; /** * CIDs of files embedded in the message. @@ -30,12 +31,12 @@ class Message /** * Create a new message instance. * - * @param \Swift_Message $swift + * @param \Symfony\Component\Mime\Email $message * @return void */ - public function __construct($swift) + public function __construct(Email $message) { - $this->swift = $swift; + $this->message = $message; } /** @@ -47,7 +48,9 @@ public function __construct($swift) */ public function from($address, $name = null) { - $this->swift->setFrom($address, $name); + is_array($address) + ? $this->message->from(...$address) + : $this->message->from(new Address($address, (string) $name)); return $this; } @@ -61,7 +64,9 @@ public function from($address, $name = null) */ public function sender($address, $name = null) { - $this->swift->setSender($address, $name); + is_array($address) + ? $this->message->sender(...$address) + : $this->message->sender(new Address($address, (string) $name)); return $this; } @@ -74,7 +79,7 @@ public function sender($address, $name = null) */ public function returnPath($address) { - $this->swift->setReturnPath($address); + $this->message->returnPath($address); return $this; } @@ -90,7 +95,9 @@ public function returnPath($address) public function to($address, $name = null, $override = false) { if ($override) { - $this->swift->setTo($address, $name); + is_array($address) + ? $this->message->to(...$address) + : $this->message->to(new Address($address, (string) $name)); return $this; } @@ -109,7 +116,9 @@ public function to($address, $name = null, $override = false) public function cc($address, $name = null, $override = false) { if ($override) { - $this->swift->setCc($address, $name); + is_array($address) + ? $this->message->cc(...$address) + : $this->message->cc(new Address($address, (string) $name)); return $this; } @@ -128,7 +137,9 @@ public function cc($address, $name = null, $override = false) public function bcc($address, $name = null, $override = false) { if ($override) { - $this->swift->setBcc($address, $name); + is_array($address) + ? $this->message->bcc(...$address) + : $this->message->bcc(new Address($address, (string) $name)); return $this; } @@ -159,9 +170,19 @@ public function replyTo($address, $name = null) protected function addAddresses($address, $name, $type) { if (is_array($address)) { - $this->swift->{"set{$type}"}($address, $name); + $type = lcfirst($type); + + $addresses = collect($address)->map(function (string|array $address) { + if (is_array($address)) { + return new Address($address['email'] ?? $address['address'], $address['name'] ?? null); + } + + return $address; + })->all(); + + $this->message->{"{$type}"}(...$addresses); } else { - $this->swift->{"add{$type}"}($address, $name); + $this->message->{"add{$type}"}(new Address($address, (string) $name)); } return $this; @@ -175,7 +196,7 @@ protected function addAddresses($address, $name, $type) */ public function subject($subject) { - $this->swift->setSubject($subject); + $this->message->subject($subject); return $this; } @@ -188,7 +209,7 @@ public function subject($subject) */ public function priority($level) { - $this->swift->setPriority($level); + $this->message->priority($level); return $this; } @@ -202,20 +223,9 @@ public function priority($level) */ public function attach($file, array $options = []) { - $attachment = $this->createAttachmentFromPath($file); - - return $this->prepAttachment($attachment, $options); - } + $this->message->attachFromPath($file, $options['as'] ?? null, $options['mime'] ?? null); - /** - * Create a Swift Attachment instance. - * - * @param string $file - * @return \Swift_Mime_Attachment - */ - protected function createAttachmentFromPath($file) - { - return Swift_Attachment::fromPath($file); + return $this; } /** @@ -228,21 +238,9 @@ protected function createAttachmentFromPath($file) */ public function attachData($data, $name, array $options = []) { - $attachment = $this->createAttachmentFromData($data, $name); + $this->message->attach($data, $name, $options['mime'] ?? null); - return $this->prepAttachment($attachment, $options); - } - - /** - * Create a Swift Attachment instance from data. - * - * @param string $data - * @param string $name - * @return \Swift_Attachment - */ - protected function createAttachmentFromData($data, $name) - { - return new Swift_Attachment($data, $name); + return $this; } /** @@ -253,13 +251,11 @@ protected function createAttachmentFromData($data, $name) */ public function embed($file) { - if (isset($this->embeddedFiles[$file])) { - return $this->embeddedFiles[$file]; - } + $cid = Str::random(10); - return $this->embeddedFiles[$file] = $this->swift->embed( - Swift_Image::fromPath($file) - ); + $this->message->embedFromPath($file, $cid); + + return "cid:$cid"; } /** @@ -272,51 +268,23 @@ public function embed($file) */ public function embedData($data, $name, $contentType = null) { - $image = new Swift_Image($data, $name, $contentType); - - return $this->swift->embed($image); - } - - /** - * Prepare and attach the given attachment. - * - * @param \Swift_Attachment $attachment - * @param array $options - * @return $this - */ - protected function prepAttachment($attachment, $options = []) - { - // First we will check for a MIME type on the message, which instructs the - // mail client on what type of attachment the file is so that it may be - // downloaded correctly by the user. The MIME option is not required. - if (isset($options['mime'])) { - $attachment->setContentType($options['mime']); - } + $this->message->embed($data, $name, $contentType); - // If an alternative name was given as an option, we will set that on this - // attachment so that it will be downloaded with the desired names from - // the developer, otherwise the default file names will get assigned. - if (isset($options['as'])) { - $attachment->setFilename($options['as']); - } - - $this->swift->attach($attachment); - - return $this; + return "cid:$name"; } /** - * Get the underlying Swift Message instance. + * Get the underlying Symfony Email instance. * - * @return \Swift_Message + * @return \Symfony\Component\Mime\Email */ - public function getSwiftMessage() + public function getSymfonyMessage() { - return $this->swift; + return $this->message; } /** - * Dynamically pass missing methods to the Swift instance. + * Dynamically pass missing methods to the Symfony instance. * * @param string $method * @param array $parameters @@ -324,6 +292,6 @@ public function getSwiftMessage() */ public function __call($method, $parameters) { - return $this->forwardCallTo($this->swift, $method, $parameters); + return $this->forwardCallTo($this->message, $method, $parameters); } } diff --git a/src/Illuminate/Mail/SentMessage.php b/src/Illuminate/Mail/SentMessage.php new file mode 100644 index 000000000000..cce42dbd009f --- /dev/null +++ b/src/Illuminate/Mail/SentMessage.php @@ -0,0 +1,54 @@ +sentMessage = $sentMessage; + } + + /** + * Get the underlying Symfony Email instance. + * + * @return \Symfony\Component\Mailer\SentMessage + */ + public function getSymfonySentMessage() + { + return $this->sentMessage; + } + + /** + * Dynamically pass missing methods to the Symfony instance. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + return $this->forwardCallTo($this->sentMessage, $method, $parameters); + } +} diff --git a/src/Illuminate/Mail/Transport/ArrayTransport.php b/src/Illuminate/Mail/Transport/ArrayTransport.php index fbedec9560aa..dc26ed69d90b 100644 --- a/src/Illuminate/Mail/Transport/ArrayTransport.php +++ b/src/Illuminate/Mail/Transport/ArrayTransport.php @@ -3,12 +3,15 @@ namespace Illuminate\Mail\Transport; use Illuminate\Support\Collection; -use Swift_Mime_SimpleMessage; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\RawMessage; -class ArrayTransport extends Transport +class ArrayTransport implements TransportInterface { /** - * The collection of Swift Messages. + * The collection of Symfony Messages. * * @var \Illuminate\Support\Collection */ @@ -27,13 +30,9 @@ public function __construct() /** * {@inheritdoc} */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) + public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { - $this->beforeSendPerformed($message); - - $this->messages[] = $message; - - return $this->numberOfRecipients($message); + return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message)); } /** @@ -55,4 +54,14 @@ public function flush() { return $this->messages = new Collection; } + + /** + * Get the string representation of the transport. + * + * @return string + */ + public function __toString(): string + { + return 'array'; + } } diff --git a/src/Illuminate/Mail/Transport/LogTransport.php b/src/Illuminate/Mail/Transport/LogTransport.php index 43a2faa204ce..d9ec8ac09d7e 100644 --- a/src/Illuminate/Mail/Transport/LogTransport.php +++ b/src/Illuminate/Mail/Transport/LogTransport.php @@ -3,10 +3,12 @@ namespace Illuminate\Mail\Transport; use Psr\Log\LoggerInterface; -use Swift_Mime_SimpleMessage; -use Swift_Mime_SimpleMimeEntity; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\RawMessage; -class LogTransport extends Transport +class LogTransport implements TransportInterface { /** * The Logger instance. @@ -29,41 +31,30 @@ public function __construct(LoggerInterface $logger) /** * {@inheritdoc} */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) + public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { - $this->beforeSendPerformed($message); + $this->logger->debug($message->toString()); - $this->logger->debug($this->getMimeEntityString($message)); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); + return new SentMessage($message, $envelope ?? Envelope::create($message)); } /** - * Get a loggable string out of a Swiftmailer entity. + * Get the logger for the LogTransport instance. * - * @param \Swift_Mime_SimpleMimeEntity $entity - * @return string + * @return \Psr\Log\LoggerInterface */ - protected function getMimeEntityString(Swift_Mime_SimpleMimeEntity $entity) + public function logger() { - $string = (string) $entity->getHeaders().PHP_EOL.$entity->getBody(); - - foreach ($entity->getChildren() as $children) { - $string .= PHP_EOL.PHP_EOL.$this->getMimeEntityString($children); - } - - return $string; + return $this->logger; } /** - * Get the logger for the LogTransport instance. + * Get the string representation of the transport. * - * @return \Psr\Log\LoggerInterface + * @return string */ - public function logger() + public function __toString(): string { - return $this->logger; + return 'log'; } } diff --git a/src/Illuminate/Mail/Transport/MailgunTransport.php b/src/Illuminate/Mail/Transport/MailgunTransport.php deleted file mode 100644 index 1c862b1a7f30..000000000000 --- a/src/Illuminate/Mail/Transport/MailgunTransport.php +++ /dev/null @@ -1,216 +0,0 @@ -key = $key; - $this->client = $client; - $this->endpoint = $endpoint ?? 'api.mailgun.net'; - - $this->setDomain($domain); - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) - { - $this->beforeSendPerformed($message); - - $to = $this->getTo($message); - - $bcc = $message->getBcc(); - - $message->setBcc([]); - - $response = $this->client->request( - 'POST', - "https://{$this->endpoint}/v3/{$this->domain}/messages.mime", - $this->payload($message, $to) - ); - - $messageId = $this->getMessageId($response); - - $message->getHeaders()->addTextHeader('X-Message-ID', $messageId); - $message->getHeaders()->addTextHeader('X-Mailgun-Message-ID', $messageId); - - $message->setBcc($bcc); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); - } - - /** - * Get the HTTP payload for sending the Mailgun message. - * - * @param \Swift_Mime_SimpleMessage $message - * @param string $to - * @return array - */ - protected function payload(Swift_Mime_SimpleMessage $message, $to) - { - return [ - 'auth' => [ - 'api', - $this->key, - ], - 'multipart' => [ - [ - 'name' => 'to', - 'contents' => $to, - ], - [ - 'name' => 'message', - 'contents' => $message->toString(), - 'filename' => 'message.mime', - ], - ], - ]; - } - - /** - * Get the "to" payload field for the API request. - * - * @param \Swift_Mime_SimpleMessage $message - * @return string - */ - protected function getTo(Swift_Mime_SimpleMessage $message) - { - return collect($this->allContacts($message))->map(function ($display, $address) { - return $display ? $display." <{$address}>" : $address; - })->values()->implode(','); - } - - /** - * Get all of the contacts for the message. - * - * @param \Swift_Mime_SimpleMessage $message - * @return array - */ - protected function allContacts(Swift_Mime_SimpleMessage $message) - { - return array_merge( - (array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc() - ); - } - - /** - * Get the message ID from the response. - * - * @param \Psr\Http\Message\ResponseInterface $response - * @return string - */ - protected function getMessageId($response) - { - return object_get( - json_decode($response->getBody()->getContents()), 'id' - ); - } - - /** - * Get the API key being used by the transport. - * - * @return string - */ - public function getKey() - { - return $this->key; - } - - /** - * Set the API key being used by the transport. - * - * @param string $key - * @return string - */ - public function setKey($key) - { - return $this->key = $key; - } - - /** - * Get the domain being used by the transport. - * - * @return string - */ - public function getDomain() - { - return $this->domain; - } - - /** - * Set the domain being used by the transport. - * - * @param string $domain - * @return string - */ - public function setDomain($domain) - { - return $this->domain = $domain; - } - - /** - * Get the API endpoint being used by the transport. - * - * @return string - */ - public function getEndpoint() - { - return $this->endpoint; - } - - /** - * Set the API endpoint being used by the transport. - * - * @param string $endpoint - * @return string - */ - public function setEndpoint($endpoint) - { - return $this->endpoint = $endpoint; - } -} diff --git a/src/Illuminate/Mail/Transport/SesTransport.php b/src/Illuminate/Mail/Transport/SesTransport.php deleted file mode 100644 index cae80810fd20..000000000000 --- a/src/Illuminate/Mail/Transport/SesTransport.php +++ /dev/null @@ -1,94 +0,0 @@ -ses = $ses; - $this->options = $options; - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) - { - $this->beforeSendPerformed($message); - - $result = $this->ses->sendEmail( - array_merge( - $this->options, [ - 'Content' => [ - 'Raw' => ['Data' => $message->toString()], - ], - ] - ) - ); - - $messageId = $result->get('MessageId'); - - $message->getHeaders()->addTextHeader('X-Message-ID', $messageId); - $message->getHeaders()->addTextHeader('X-SES-Message-ID', $messageId); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); - } - - /** - * Get the Amazon SES client for the SesTransport instance. - * - * @return \Aws\SesV2\SesV2Client - */ - public function ses() - { - return $this->ses; - } - - /** - * Get the transmission options being used by the transport. - * - * @return array - */ - public function getOptions() - { - return $this->options; - } - - /** - * Set the transmission options being used by the transport. - * - * @param array $options - * @return array - */ - public function setOptions(array $options) - { - return $this->options = $options; - } -} diff --git a/src/Illuminate/Mail/Transport/Transport.php b/src/Illuminate/Mail/Transport/Transport.php deleted file mode 100644 index b26bff3ff57d..000000000000 --- a/src/Illuminate/Mail/Transport/Transport.php +++ /dev/null @@ -1,108 +0,0 @@ -plugins, $plugin); - } - - /** - * Iterate through registered plugins and execute plugins' methods. - * - * @param \Swift_Mime_SimpleMessage $message - * @return void - */ - protected function beforeSendPerformed(Swift_Mime_SimpleMessage $message) - { - $event = new Swift_Events_SendEvent($this, $message); - - foreach ($this->plugins as $plugin) { - if (method_exists($plugin, 'beforeSendPerformed')) { - $plugin->beforeSendPerformed($event); - } - } - } - - /** - * Iterate through registered plugins and execute plugins' methods. - * - * @param \Swift_Mime_SimpleMessage $message - * @return void - */ - protected function sendPerformed(Swift_Mime_SimpleMessage $message) - { - $event = new Swift_Events_SendEvent($this, $message); - - foreach ($this->plugins as $plugin) { - if (method_exists($plugin, 'sendPerformed')) { - $plugin->sendPerformed($event); - } - } - } - - /** - * Get the number of recipients. - * - * @param \Swift_Mime_SimpleMessage $message - * @return int - */ - protected function numberOfRecipients(Swift_Mime_SimpleMessage $message) - { - return count(array_merge( - (array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc() - )); - } -} diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index 53e88e315bd3..a5de844a8a33 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -23,7 +23,7 @@ "illuminate/support": "^9.0", "league/commonmark": "^2.0", "psr/log": "^1.0", - "swiftmailer/swiftmailer": "^6.2.7", + "symfony/mailer": "^6.0", "tijsverkoyen/css-to-inline-styles": "^2.2.2" }, "autoload": { @@ -37,9 +37,10 @@ } }, "suggest": { - "aws/aws-sdk-php": "Required to use the SES mail driver (^3.189.0).", - "guzzlehttp/guzzle": "Required to use the Mailgun mail driver (^7.2).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).", + "symfony/http-client": "Required to use the Symfony API mail transports (^6.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Notifications/Channels/MailChannel.php b/src/Illuminate/Notifications/Channels/MailChannel.php index 2a30bcdacc84..77885b87a88e 100644 --- a/src/Illuminate/Notifications/Channels/MailChannel.php +++ b/src/Illuminate/Notifications/Channels/MailChannel.php @@ -244,7 +244,7 @@ protected function addAttachments($mailMessage, $message) protected function runCallbacks($mailMessage, $message) { foreach ($message->callbacks as $callback) { - $callback($mailMessage->getSwiftMessage()); + $callback($mailMessage->getSymfonyMessage()); } return $this; diff --git a/src/Illuminate/Notifications/Messages/MailMessage.php b/src/Illuminate/Notifications/Messages/MailMessage.php index 94342f30b2bc..24c2515ca52c 100644 --- a/src/Illuminate/Notifications/Messages/MailMessage.php +++ b/src/Illuminate/Notifications/Messages/MailMessage.php @@ -322,12 +322,12 @@ public function render() } /** - * Register a callback to be called with the Swift message instance. + * Register a callback to be called with the Symfony message instance. * * @param callable $callback * @return $this */ - public function withSwiftMessage($callback) + public function withSymfonyMessage($callback) { $this->callbacks[] = $callback; diff --git a/src/Illuminate/Support/Facades/Mail.php b/src/Illuminate/Support/Facades/Mail.php index 36796e752e55..1408480d7768 100755 --- a/src/Illuminate/Support/Facades/Mail.php +++ b/src/Illuminate/Support/Facades/Mail.php @@ -10,6 +10,10 @@ * @method static \Illuminate\Mail\PendingMail to($users) * @method static \Illuminate\Support\Collection queued(string $mailable, \Closure|string $callback = null) * @method static \Illuminate\Support\Collection sent(string $mailable, \Closure|string $callback = null) + * @method static \Illuminate\Mail\SentMessage|null raw(string $text, $callback) + * @method static \Illuminate\Mail\SentMessage|null plain(string $view, array $data, $callback) + * @method static \Illuminate\Mail\SentMessage|null html(string $html, $callback) + * @method static \Illuminate\Mail\SentMessage|null send(\Illuminate\Contracts\Mail\Mailable|string|array $view, array $data = [], \Closure|string $callback = null) * @method static array failures() * @method static bool hasQueued(string $mailable) * @method static bool hasSent(string $mailable) @@ -23,10 +27,6 @@ * @method static void assertNothingSent() * @method static void assertQueued(string|\Closure $mailable, callable|int $callback = null) * @method static void assertSent(string|\Closure $mailable, callable|int $callback = null) - * @method static void raw(string $text, $callback) - * @method static void plain(string $view, array $data, $callback) - * @method static void html(string $html, $callback) - * @method static void send(\Illuminate\Contracts\Mail\Mailable|string|array $view, array $data = [], \Closure|string $callback = null) * * @see \Illuminate\Mail\Mailer * @see \Illuminate\Support\Testing\Fakes\MailFake diff --git a/tests/Integration/Mail/SendingMailWithLocaleTest.php b/tests/Integration/Mail/SendingMailWithLocaleTest.php index 5421e308d460..0031461f2e96 100644 --- a/tests/Integration/Mail/SendingMailWithLocaleTest.php +++ b/tests/Integration/Mail/SendingMailWithLocaleTest.php @@ -44,7 +44,7 @@ public function testMailIsSentWithDefaultLocale() Mail::to('test@mail.com')->send(new TestMail); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -53,7 +53,7 @@ public function testMailIsSentWithSelectedLocale() Mail::to('test@mail.com')->locale('ar')->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -65,7 +65,7 @@ public function testMailIsSentWithLocaleFromMailable() Mail::to('test@mail.com')->send($mailable); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -79,8 +79,8 @@ public function testMailIsSentWithLocaleUpdatedListenersCalled() Mail::to('test@mail.com')->locale('es')->send(new TimestampTestMail); - Assert::assertMatchesRegularExpression('/nombre (en|dentro de) (un|1) día/', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + Assert::assertMatchesRegularExpression('/nombre (en|dentro de) (un|1) d=C3=ADa/', + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertSame('en', Carbon::getLocale()); @@ -96,7 +96,7 @@ public function testLocaleIsSentWithModelPreferredLocale() Mail::to($recipient)->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -110,7 +110,7 @@ public function testLocaleIsSentWithSelectedLocaleOverridingModelPreferredLocale Mail::to($recipient)->locale('ar')->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -129,7 +129,7 @@ public function testLocaleIsSentWithModelPreferredLocaleWillIgnorePreferredLocal Mail::to($toRecipient)->cc($ccRecipient)->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -149,7 +149,7 @@ public function testLocaleIsNotSentWithModelPreferredLocaleWhenThereAreMultipleR Mail::to($recipients)->send(new TestMail); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -161,11 +161,11 @@ public function testLocaleIsSetBackToDefaultAfterMailSent() $this->assertSame('en', app('translator')->getLocale()); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); } } diff --git a/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php index c474742c3538..f309390f0d5a 100644 --- a/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php +++ b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php @@ -76,7 +76,7 @@ public function testMailIsSentWithDefaultLocale() NotificationFacade::send($user, new GreetingMailNotification); $this->assertStringContainsString('hello', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -90,7 +90,7 @@ public function testMailIsSentWithFacadeSelectedLocale() NotificationFacade::locale('fr')->send($user, new GreetingMailNotification); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -110,11 +110,11 @@ public function testMailIsSentWithNotificationSelectedLocale() NotificationFacade::send($users, (new GreetingMailNotification)->locale('fr')); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); } @@ -128,7 +128,7 @@ public function testMailableIsSentWithSelectedLocale() NotificationFacade::locale('fr')->send($user, new GreetingMailNotificationWithMailable); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -148,11 +148,11 @@ public function testMailIsSentWithLocaleUpdatedListenersCalled() $user->notify((new GreetingMailNotification)->locale('fr')); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); Assert::assertMatchesRegularExpression('/dans (1|un) jour/', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertTrue($this->app->isLocale('en')); @@ -170,7 +170,7 @@ public function testLocaleIsSentWithNotifiablePreferredLocale() $recipient->notify(new GreetingMailNotification); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -195,13 +195,13 @@ public function testLocaleIsSentWithNotifiablePreferredLocaleForMultipleRecipien ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('hola', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); $this->assertStringContainsString('hello', - app('mailer')->getSwiftMailer()->getTransport()->messages()[2]->getBody() + app('mailer')->getSymfonyTransport()->messages()[2]->toString() ); } @@ -217,7 +217,7 @@ public function testLocaleIsSentWithNotificationSelectedLocaleOverridingNotifiab ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -233,7 +233,7 @@ public function testLocaleIsSentWithFacadeSelectedLocaleOverridingNotifiablePref ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } } @@ -285,7 +285,8 @@ public function via($notifiable) public function toMail($notifiable) { - return new GreetingMailable; + return (new GreetingMailable) + ->to($notifiable->email); } } diff --git a/tests/Mail/MailFailoverTransportTest.php b/tests/Mail/MailFailoverTransportTest.php index 6d8c8fec8bd9..15f9c0ed02a9 100644 --- a/tests/Mail/MailFailoverTransportTest.php +++ b/tests/Mail/MailFailoverTransportTest.php @@ -2,8 +2,8 @@ namespace Illuminate\Tests\Mail; -use Illuminate\Mail\Transport\ArrayTransport; use Orchestra\Testbench\TestCase; +use Symfony\Component\Mailer\Transport\FailoverTransport; class MailFailoverTransportTest extends TestCase { @@ -30,14 +30,8 @@ public function testGetFailoverTransportWithConfiguredTransports() ], ]); - $transport = app('mailer')->getSwiftMailer()->getTransport(); - $this->assertInstanceOf(\Swift_FailoverTransport::class, $transport); - - $transports = $transport->getTransports(); - $this->assertCount(2, $transports); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $transports[0]); - $this->assertEquals('/usr/sbin/sendmail -bs', $transports[0]->getCommand()); - $this->assertInstanceOf(ArrayTransport::class, $transports[1]); + $transport = app('mailer')->getSymfonyTransport(); + $this->assertInstanceOf(FailoverTransport::class, $transport); } public function testGetFailoverTransportWithLaravel6StyleMailConfiguration() @@ -51,13 +45,7 @@ public function testGetFailoverTransportWithLaravel6StyleMailConfiguration() $this->app['config']->set('mail.sendmail', '/usr/sbin/sendmail -bs'); - $transport = app('mailer')->getSwiftMailer()->getTransport(); - $this->assertInstanceOf(\Swift_FailoverTransport::class, $transport); - - $transports = $transport->getTransports(); - $this->assertCount(2, $transports); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $transports[0]); - $this->assertEquals('/usr/sbin/sendmail -bs', $transports[0]->getCommand()); - $this->assertInstanceOf(ArrayTransport::class, $transports[1]); + $transport = app('mailer')->getSymfonyTransport(); + $this->assertInstanceOf(FailoverTransport::class, $transport); } } diff --git a/tests/Mail/MailLogTransportTest.php b/tests/Mail/MailLogTransportTest.php index 5848734d2eec..9062347c9a54 100644 --- a/tests/Mail/MailLogTransportTest.php +++ b/tests/Mail/MailLogTransportTest.php @@ -22,7 +22,7 @@ public function testGetLogTransportWithConfiguredChannel() 'path' => 'mail.log', ]); - $transport = app('mailer')->getSwiftMailer()->getTransport(); + $transport = app('mailer')->getSymfonyTransport(); $this->assertInstanceOf(LogTransport::class, $transport); $logger = $transport->logger(); @@ -30,15 +30,16 @@ public function testGetLogTransportWithConfiguredChannel() $this->assertInstanceOf(Logger::class, $monolog = $logger->getLogger()); $this->assertCount(1, $handlers = $monolog->getHandlers()); - $this->assertInstanceOf(StreamHandler::class, $handler = $handlers[0]); + $this->assertInstanceOf(StreamHandler::class, $handlers[0]); } public function testGetLogTransportWithPsrLogger() { $this->app['config']->set('mail.driver', 'log'); + $logger = $this->app->instance('log', new NullLogger); - $transportLogger = app('mailer')->getSwiftMailer()->getTransport()->logger(); + $transportLogger = app('mailer')->getSymfonyTransport()->logger(); $this->assertEquals($logger, $transportLogger); } diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index b14b9ba285cd..5efc1b7019eb 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -7,194 +7,178 @@ use Illuminate\Mail\Events\MessageSending; use Illuminate\Mail\Events\MessageSent; use Illuminate\Mail\Mailer; +use Illuminate\Mail\Message; +use Illuminate\Mail\Transport\ArrayTransport; use Illuminate\Support\HtmlString; use Mockery as m; use PHPUnit\Framework\TestCase; -use stdClass; -use Swift_Mailer; -use Swift_Message; -use Swift_Mime_SimpleMessage; -use Swift_Transport; class MailMailerTest extends TestCase { protected function tearDown(): void { + unset($_SERVER['__mailer.test']); + m::close(); } public function testMailerSendSendsMessageWithProperViewContent() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send('foo', ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $this->assertStringContainsString('rendered.view', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperViewContentUsingHtmlStrings() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->never(); + $view = m::mock(Factory::class); $view->shouldReceive('render')->never(); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('addPart')->once()->with('rendered.text', 'text/plain'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['html' => new HtmlString('rendered.view'), 'text' => new HtmlString('rendered.text')], ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; - }); - unset($_SERVER['__mailer.test']); + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send( + ['html' => new HtmlString('
Hello Laravel
'), 'text' => new HtmlString('Hello World')], + ['data'], + function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); + } + ); + + $this->assertStringContainsString('Hello Laravel
', $sentMessage->toString()); + $this->assertStringContainsString('Hello World', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperViewContentUsingHtmlMethod() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->never(); + $view = m::mock(Factory::class); $view->shouldReceive('render')->never(); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->html('rendered.view', function ($m) { - $_SERVER['__mailer.test'] = $m; + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->html('Hello World
', function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $this->assertStringContainsString('Hello World
', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperPlainViewContent() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('bar', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->twice()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('addPart')->once()->with('rendered.view', 'text/plain'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['foo', 'bar'], ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; + $view = m::mock(Factory::class); + $view->shouldReceive('make')->twice()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + $view->shouldReceive('render')->once()->andReturn('rendered.plain'); + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send(['foo', 'bar'], ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $expected = <<