From c8013004a27aef734e9165db5aca601f0feaa8fe Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 25 Jun 2025 15:49:47 +0700 Subject: [PATCH 01/31] fix merge error --- src/Illuminate/Mail/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index a6038462..71e648da 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -13,7 +13,8 @@ "illuminate/log": "4.2.*", "illuminate/support": "4.2.*", "illuminate/view": "4.2.*", - "swiftmailer/swiftmailer": "^6.0" + "swiftmailer/swiftmailer": "^6.0", + "symfony/mailer": "^6.4" }, "require-dev": { "illuminate/queue": "4.2.*" From 9a131104cb979f8a23f1b5ea04b09d5e41193643 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 25 Jun 2025 15:50:37 +0700 Subject: [PATCH 02/31] fix merge error --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 998111cd..3c2c560b 100755 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ "symfony/finder": "~6.4", "symfony/http-foundation": "~6.4", "symfony/http-kernel": "~6.4", + "symfony/mailer": "^6.4", "symfony/mime": "~6.4", "symfony/process": "~6.4", "symfony/routing": "~6.4", From b11022fe4a2066ef7348610c7c1a86e3cac63ddc Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 11:54:31 +0700 Subject: [PATCH 03/31] refactor message structure to accept symfony email --- src/Illuminate/Mail/Message.php | 162 +++++++++++--------------------- 1 file changed, 55 insertions(+), 107 deletions(-) diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index 40855718..7ac90572 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -1,39 +1,43 @@ swift = $swift; + $this->message = $message; } /** * Add a "from" address to the message. * * @param string $address - * @param string $name + * @param ?string $name * @return $this */ - public function from($address, $name = null) + public function from(string $address, ?string $name = null): self { - $this->swift->setFrom($address, $name); - + $this->message->from(new Address($address, $name)); return $this; } @@ -41,12 +45,12 @@ public function from($address, $name = null) * Set the "sender" of the message. * * @param string $address - * @param string $name + * @param ?string $name * @return $this */ - public function sender($address, $name = null) + public function sender(string $address, ?string $name = null): self { - $this->swift->setSender($address, $name); + $this->message->sender(new Address($address, $name)); return $this; } @@ -57,10 +61,9 @@ public function sender($address, $name = null) * @param string $address * @return $this */ - public function returnPath($address) + public function returnPath(string $address): self { - $this->swift->setReturnPath($address); - + $this->message->returnPath($address); return $this; } @@ -68,10 +71,10 @@ public function returnPath($address) * Add a recipient to the message. * * @param string|array $address - * @param string $name + * @param ?string $name * @return $this */ - public function to($address, $name = null) + public function to(string|array $address, ?string $name = null): self { return $this->addAddresses($address, $name, 'To'); } @@ -83,7 +86,7 @@ public function to($address, $name = null) * @param string $name * @return $this */ - public function cc($address, $name = null) + public function cc(string $address, ?string $name = null): self { return $this->addAddresses($address, $name, 'Cc'); } @@ -95,7 +98,7 @@ public function cc($address, $name = null) * @param string $name * @return $this */ - public function bcc($address, $name = null) + public function bcc(string $address, ?string $name = null): self { return $this->addAddresses($address, $name, 'Bcc'); } @@ -107,7 +110,7 @@ public function bcc($address, $name = null) * @param string $name * @return $this */ - public function replyTo($address, $name = null) + public function replyTo(string $address, ?string $name = null): self { return $this->addAddresses($address, $name, 'ReplyTo'); } @@ -120,15 +123,16 @@ public function replyTo($address, $name = null) * @param string $type * @return $this */ - protected function addAddresses($address, $name, $type) + protected function addAddresses(string|array $address, string $name, string $type): self { if (is_array($address)) { - $this->swift->{"set{$type}"}($address, $name); + $type = lcfirst($type); + $this->message->$type($address, $name); } else { - $this->swift->{"add{$type}"}($address, $name); + $this->message->{"add{$type}"}($address, $name); } return $this; @@ -140,10 +144,9 @@ protected function addAddresses($address, $name, $type) * @param string $subject * @return $this */ - public function subject($subject) + public function subject(string $subject): self { - $this->swift->setSubject($subject); - + $this->message->subject($subject); return $this; } @@ -153,10 +156,9 @@ public function subject($subject) * @param int $level * @return $this */ - public function priority($level) + public function priority(int $level): self { - $this->swift->setPriority($level); - + $this->message->priority($level); return $this; } @@ -167,22 +169,10 @@ public function priority($level) * @param array $options * @return $this */ - public function attach($file, array $options = array()) + public function attach(string $file, array $options = []): self { - $attachment = $this->createAttachmentFromPath($file); - - return $this->prepAttachment($attachment, $options); - } - - /** - * Create a Swift Attachment instance. - * - * @param string $file - * @return \Swift_Attachment - */ - protected function createAttachmentFromPath($file) - { - return Swift_Attachment::fromPath($file); + $this->message->attachFromPath($file, $options['as'] ?? null, $options['mime'] ?? null); + return $this; } /** @@ -193,23 +183,10 @@ protected function createAttachmentFromPath($file) * @param array $options * @return $this */ - public function attachData($data, $name, array $options = array()) - { - $attachment = $this->createAttachmentFromData($data, $name); - - 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) + public function attachData(string $data, string $name, array $options = []): self { - return Swift_Attachment::newInstance($data, $name); + $this->message->attach($data, $name, $options['mime'] ?? null); + return $this; } /** @@ -218,9 +195,11 @@ protected function createAttachmentFromData($data, $name) * @param string $file * @return string */ - public function embed($file) + public function embed(string $file): string { - return $this->swift->embed(Swift_Image::fromPath($file)); + $cid = Str::random(10); + $this->message->embedFromPath($file, $cid); + return "cid:$cid"; } /** @@ -231,63 +210,32 @@ public function embed($file) * @param string $contentType * @return string */ - public function embedData($data, $name, $contentType = null) + public function embedData(string $data, string $name, ?string $contentType = null): string { - $image = Swift_Image::newInstance($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 = array()) - { - // 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']); - } - - // 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; + $this->message->embed($data, $name, $contentType); + return "cid:$name"; } /** - * Get the underlying Swift Message instance. + * Get the underlying Symfony Message instance. * - * @return \Swift_Message + * @return Email */ - public function getSwiftMessage() + public function getSymfonyMessage(): Email { - return $this->swift; + return $this->message; } /** - * Dynamically pass missing methods to the Swift instance. + * Dynamically pass missing methods to the Symfony Message instance. * * @param string $method * @param array $parameters * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters) { - $callable = array($this->swift, $method); + $callable = [$this->message, $method]; return call_user_func_array($callable, $parameters); } From 943b95fac88d9706fd009a0c67485cf2119d9993 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 11:54:51 +0700 Subject: [PATCH 04/31] refactor test --- tests/Mail/MailMessageTest.php | 61 ++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/tests/Mail/MailMessageTest.php b/tests/Mail/MailMessageTest.php index bf0474b8..c03480f1 100755 --- a/tests/Mail/MailMessageTest.php +++ b/tests/Mail/MailMessageTest.php @@ -3,9 +3,31 @@ use Illuminate\Mail\Message; use L4\Tests\BackwardCompatibleTestCase; use Mockery as m; +use Symfony\Component\Mime\Email; class MailMessageTest extends BackwardCompatibleTestCase { + private static string $staticFilePath; + + private Message $message; + + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + file_put_contents(self::$staticFilePath = __DIR__.'/foo.jpg', 'expected attachment body'); + } + + public static function tearDownAfterClass(): void + { + parent::tearDownAfterClass(); + @unlink(self::$staticFilePath); + } + + protected function setUp(): void + { + parent::setUp(); + $this->message = new Message(new Email()); + } protected function tearDown(): void { @@ -13,32 +35,29 @@ protected function tearDown(): void } - public function testBasicAttachment() + public function testBasicAttachment(): void { - $swift = m::mock('StdClass'); - $message = $this->getMock(Message::class, ['createAttachmentFromPath'], [$swift]); - $attachment = m::mock('StdClass'); - $message->expects($this->once())->method('createAttachmentFromPath')->with($this->equalTo('foo.jpg'))->willReturn( - $attachment - ); - $swift->shouldReceive('attach')->once()->with($attachment); - $attachment->shouldReceive('setContentType')->once()->with('image/jpeg'); - $attachment->shouldReceive('setFilename')->once()->with('bar.jpg'); - $message->attach('foo.jpg', ['mime' => 'image/jpeg', 'as' => 'bar.jpg']); + $this->message->attach(self::$staticFilePath, ['as' => 'bar.jpg', 'mime' => 'image/jpg']); + + $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; + $headers = $attachment->getPreparedHeaders()->toArray(); + self::assertSame('expected attachment body', $attachment->getBody()); + self::assertSame('Content-Type: image/jpg; name=bar.jpg', $headers[0]); + self::assertSame('Content-Transfer-Encoding: base64', $headers[1]); + self::assertSame('Content-Disposition: attachment; name=bar.jpg; filename=bar.jpg', $headers[2]); } - public function testDataAttachment() + public function testDataAttachment(): void { - $swift = m::mock('StdClass'); - $message = $this->getMock(Message::class, ['createAttachmentFromData'], [$swift]); - $attachment = m::mock('StdClass'); - $message->expects($this->once())->method('createAttachmentFromData')->with($this->equalTo('foo'), $this->equalTo('name'))->willReturn( - $attachment - ); - $swift->shouldReceive('attach')->once()->with($attachment); - $attachment->shouldReceive('setContentType')->once()->with('image/jpeg'); - $message->attachData('foo', 'name', ['mime' => 'image/jpeg']); + $this->message->attachData('expected attachment body', 'foo.jpg', ['mime' => 'image/jpg']); + + $attachment = $this->message->getSymfonyMessage()->getAttachments()[0]; + $headers = $attachment->getPreparedHeaders()->toArray(); + self::assertSame('expected attachment body', $attachment->getBody()); + self::assertSame('Content-Type: image/jpg; name=foo.jpg', $headers[0]); + self::assertSame('Content-Transfer-Encoding: base64', $headers[1]); + self::assertSame('Content-Disposition: attachment; name=foo.jpg; filename=foo.jpg', $headers[2]); } } From 9568e323806b5ef13fd04d8538c23c17a475761e Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 13:43:44 +0700 Subject: [PATCH 05/31] add refactored log transport --- .../Mail/Transport/LogTransportV2.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/Illuminate/Mail/Transport/LogTransportV2.php diff --git a/src/Illuminate/Mail/Transport/LogTransportV2.php b/src/Illuminate/Mail/Transport/LogTransportV2.php new file mode 100644 index 00000000..9d85d23a --- /dev/null +++ b/src/Illuminate/Mail/Transport/LogTransportV2.php @@ -0,0 +1,41 @@ +logger = $logger; + } + + /** + * @inheritDoc + */ + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage + { + $this->logger->debug($message->toString()); + return new SentMessage($message, $envelope ?? Envelope::create($message)); + } + + public function logger(): LoggerInterface + { + return $this->logger; + } + + /** + * @inheritDoc + */ + public function __toString(): string + { + return 'log'; + } +} \ No newline at end of file From 8076b28baa1f2d61296522ddbe345f85dc76eff1 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 14:12:30 +0700 Subject: [PATCH 06/31] add forwardscalls --- .../Support/Traits/ForwardsCalls.php | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/Illuminate/Support/Traits/ForwardsCalls.php diff --git a/src/Illuminate/Support/Traits/ForwardsCalls.php b/src/Illuminate/Support/Traits/ForwardsCalls.php new file mode 100644 index 00000000..e7181809 --- /dev/null +++ b/src/Illuminate/Support/Traits/ForwardsCalls.php @@ -0,0 +1,75 @@ +{$method}(...$parameters); + } catch (Error|BadMethodCallException $e) { + $pattern = '~^Call to undefined method (?P[^:]+)::(?P[^\(]+)\(\)$~'; + + if (! preg_match($pattern, $e->getMessage(), $matches)) { + throw $e; + } + + if ($matches['class'] != get_class($object) || + $matches['method'] != $method) { + throw $e; + } + + static::throwBadMethodCallException($method); + } + } + + /** + * Forward a method call to the given object, returning $this if the forwarded call returned itself. + * + * @param mixed $object + * @param string $method + * @param array $parameters + * @return mixed + * + * @throws \BadMethodCallException + */ + protected function forwardDecoratedCallTo($object, $method, $parameters) + { + $result = $this->forwardCallTo($object, $method, $parameters); + + if ($result === $object) { + return $this; + } + + return $result; + } + + /** + * Throw a bad method call exception for the given method. + * + * @param string $method + * @return void + * + * @throws \BadMethodCallException + */ + protected static function throwBadMethodCallException($method) + { + throw new BadMethodCallException(sprintf( + 'Call to undefined method %s::%s()', static::class, $method + )); + } +} From 72aae99a0b37a01672587cf032012e551bee9917 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 14:13:37 +0700 Subject: [PATCH 07/31] add sent message --- src/Illuminate/Mail/SentMessage.php | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/Illuminate/Mail/SentMessage.php diff --git a/src/Illuminate/Mail/SentMessage.php b/src/Illuminate/Mail/SentMessage.php new file mode 100644 index 00000000..cce42dbd --- /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); + } +} From aa07573608bb29f42aac2c4ce16f28bda2ecbf7c Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 16 Jul 2024 14:48:12 +0700 Subject: [PATCH 08/31] refactor mailer --- src/Illuminate/Mail/Mailer.php | 245 ++++++++++++++------------------- 1 file changed, 105 insertions(+), 140 deletions(-) diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index a83b8de7..ac31a5ee 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -2,110 +2,86 @@ use Closure; use Illuminate\Support\Str; -use Swift_Mailer; -use Swift_Message; use Illuminate\Log\Writer; use Illuminate\View\Factory; use Illuminate\Events\Dispatcher; use Illuminate\Queue\QueueManager; use Illuminate\Container\Container; use Illuminate\Support\SerializableClosure; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; class Mailer { /** * The view factory instance. - * - * @var \Illuminate\View\Factory */ - protected $views; + protected Factory $views; /** - * The Swift Mailer instance. - * - * @var \Swift_Mailer + * The Symfony Transport instance. */ - protected $swift; + protected TransportInterface $transport; /** * The event dispatcher instance. - * - * @var \Illuminate\Events\Dispatcher */ - protected $events; + protected ?Dispatcher $events; /** * The global from address and name. - * - * @var array */ - protected $from; + protected array $from; /** * The log writer instance. - * - * @var \Illuminate\Log\Writer */ - protected $logger; + protected Writer $logger; /** * The IoC container instance. - * - * @var \Illuminate\Container\Container */ - protected $container; + protected Container $container; /* * The QueueManager instance. - * - * @var \Illuminate\Queue\QueueManager */ - protected $queue; + protected QueueManager $queue; /** * Indicates if the actual sending is disabled. - * - * @var bool */ - protected $pretending = false; + protected bool $pretending = false; /** * Array of failed recipients. - * - * @var array */ - protected $failedRecipients = array(); + protected array $failedRecipients = []; /** * Array of parsed views containing html and text view name. - * - * @var array */ - protected $parsedViews = array(); + protected array $parsedViews = []; /** * Create a new Mailer instance. - * - * @param \Illuminate\View\Factory $views - * @param \Swift_Mailer $swift - * @param \Illuminate\Events\Dispatcher $events - * @return void */ - public function __construct(Factory $views, Swift_Mailer $swift, Dispatcher $events = null) + public function __construct(Factory $views, TransportInterface $transport, ?Dispatcher $events = null) { $this->views = $views; - $this->swift = $swift; + $this->transport = $transport; $this->events = $events; } - /** - * Set the global from address and name. - * - * @param string $address - * @param string $name - * @return void - */ - public function alwaysFrom($address, $name = null) + /** + * Set the global from address and name. + * @param string $address + * @param ?string $name + * @return void + */ + public function alwaysFrom(string $address, ?string $name = null): void { $this->from = compact('address', 'name'); } @@ -118,20 +94,20 @@ public function alwaysFrom($address, $name = null) * @param mixed $callback * @return int */ - public function plain($view, array $data, $callback) + public function plain(string $view, array $data, mixed $callback): int { - return $this->send(array('text' => $view), $data, $callback); + return $this->send(['text' => $view], $data, $callback); } /** * Send a new message using a view. * - * @param string|array $view + * @param array|string $view * @param array $data - * @param \Closure|string $callback + * @param string|\Closure $callback * @return void */ - public function send($view, array $data, $callback) + public function send(array|string $view, array $data, string|Closure $callback): void { // First we need to parse the view, which could either be a string or an array // containing both an HTML and plain text versions of the view which should @@ -147,22 +123,21 @@ public function send($view, array $data, $callback) // to creating view based emails that are able to receive arrays of data. $this->addContent($message, $view, $plain, $data); - $message = $message->getSwiftMessage(); - - $this->sendSwiftMessage($message); + $message = $message->getSymfonyMessage(); + $this->sendSymfonyMessage($message); } /** * Queue a new e-mail message for sending. * - * @param string|array $view + * @param array|string $view * @param array $data - * @param \Closure|string $callback - * @param string $queue + * @param string|\Closure $callback + * @param string|null $queue * @return mixed */ - public function queue($view, array $data, $callback, $queue = null) - { + public function queue(array|string $view, array $data, string|Closure $callback, ?string $queue = null): mixed + { $callback = $this->buildQueueCallable($callback); return $this->queue->push('mailer@handleQueuedMessage', compact('view', 'data', 'callback'), $queue); @@ -171,29 +146,29 @@ public function queue($view, array $data, $callback, $queue = null) /** * Queue a new e-mail message for sending on the given queue. * - * @param string $queue - * @param string|array $view + * @param string $queue + * @param array|string $view * @param array $data - * @param \Closure|string $callback + * @param string|\Closure $callback * @return mixed */ - public function queueOn($queue, $view, array $data, $callback) - { + public function queueOn(string $queue, array|string $view, array $data, string|Closure $callback): mixed + { return $this->queue($view, $data, $callback, $queue); } /** * Queue a new e-mail message for sending after (n) seconds. * - * @param int $delay + * @param int $delay * @param string|array $view * @param array $data * @param \Closure|string $callback * @param string $queue * @return mixed */ - public function later($delay, $view, array $data, $callback, $queue = null) - { + public function later(int $delay, $view, array $data, $callback, $queue = null): mixed + { $callback = $this->buildQueueCallable($callback); return $this->queue->later($delay, 'mailer@handleQueuedMessage', compact('view', 'data', 'callback'), $queue); @@ -209,8 +184,8 @@ public function later($delay, $view, array $data, $callback, $queue = null) * @param \Closure|string $callback * @return mixed */ - public function laterOn($queue, $delay, $view, array $data, $callback) - { + public function laterOn($queue, $delay, $view, array $data, $callback): mixed + { return $this->later($delay, $view, $data, $callback, $queue); } @@ -220,8 +195,8 @@ public function laterOn($queue, $delay, $view, array $data, $callback) * @param mixed $callback * @return mixed */ - protected function buildQueueCallable($callback) - { + protected function buildQueueCallable($callback): mixed + { if ( ! $callback instanceof Closure) return $callback; return serialize(new SerializableClosure($callback)); @@ -234,8 +209,8 @@ protected function buildQueueCallable($callback) * @param array $data * @return void */ - public function handleQueuedMessage($job, $data) - { + public function handleQueuedMessage($job, $data): void + { $this->send($data['view'], $data['data'], $this->getQueuedCallable($data)); $job->delete(); @@ -247,8 +222,8 @@ public function handleQueuedMessage($job, $data) * @param array $data * @return mixed */ - protected function getQueuedCallable(array $data) - { + protected function getQueuedCallable(array $data): mixed + { if (Str::contains($data['callback'], 'SerializableClosure')) { return with(unserialize($data['callback']))->getClosure(); @@ -266,8 +241,8 @@ protected function getQueuedCallable(array $data) * @param array $data * @return void */ - protected function addContent($message, $view, $plain, $data) - { + protected function addContent($message, $view, $plain, $data): void + { if (isset($view)) { $message->setBody($this->getView($view, $data), 'text/html'); @@ -287,8 +262,8 @@ protected function addContent($message, $view, $plain, $data) * * @throws \InvalidArgumentException */ - protected function parseView($view) - { + protected function parseView($view): array + { if (is_string($view)) return array($view, null); // If the given view is an array with numeric keys, we will just assume that @@ -312,38 +287,32 @@ protected function parseView($view) throw new \InvalidArgumentException("Invalid view."); } - /** - * Send a Swift Message instance. - * - * @param \Swift_Message $message - * @return void - */ - protected function sendSwiftMessage($message) - { - if ($this->events) - { - $this->events->fire('mailer.sending', array($message)); - } + /** + * Send a Symfony Message. + */ + protected function sendSymfonyMessage(Email $message): void + { + $this->events?->fire('mailer.sending', [$message]); - if ( ! $this->pretending) - { - $this->swift->send($message, $this->failedRecipients); - } - elseif (isset($this->logger)) - { - $this->logMessage($message); - } - } + if (!$this->pretending) + { + $this->transport->send($message, Envelope::create($message)); + } + elseif (isset($this->logger)) + { + $this->logMessage($message); + } + } /** * Log that a message was sent. - * - * @param \Swift_Message $message - * @return void */ - protected function logMessage($message) + protected function logMessage(Email $message): void { - $emails = implode(', ', array_keys((array) $message->getTo())); + $emails = implode(', ', array_map( + fn (Address $address) => $address->getAddress(), + $message->getTo() + )); $this->logger->info("Pretending to mail message to: {$emails}"); } @@ -351,14 +320,14 @@ protected function logMessage($message) /** * Call the provided message builder. * - * @param \Closure|string $callback - * @param \Illuminate\Mail\Message $message + * @param string|\Closure $callback + * @param \Illuminate\Mail\Message $message * @return mixed * * @throws \InvalidArgumentException */ - protected function callMessageBuilder($callback, $message) - { + protected function callMessageBuilder(string|Closure $callback, Message $message): mixed + { if ($callback instanceof Closure) { return call_user_func($callback, $message); @@ -376,9 +345,9 @@ protected function callMessageBuilder($callback, $message) * * @return \Illuminate\Mail\Message */ - protected function createMessage() - { - $message = new Message(new Swift_Message); + protected function createMessage(): Message + { + $message = new Message(new Email()); // If a global from address has been specified we will set it on every message // instances so the developer does not have to repeat themselves every time @@ -399,7 +368,7 @@ protected function createMessage() * @return \Illuminate\View\View */ protected function getView($view, $data) - { + { return $this->views->make($view, $data)->render(); } @@ -409,8 +378,8 @@ protected function getView($view, $data) * @param bool $value * @return void */ - public function pretend($value = true) - { + public function pretend($value = true): void + { $this->pretending = $value; } @@ -419,8 +388,8 @@ public function pretend($value = true) * * @return bool */ - public function isPretending() - { + public function isPretending(): bool + { return $this->pretending; } @@ -429,40 +398,36 @@ public function isPretending() * * @return \Illuminate\View\Factory */ - public function getViewFactory() - { + public function getViewFactory(): Factory + { return $this->views; } /** - * Get the Swift Mailer instance. - * - * @return \Swift_Mailer + * Get the Symfony Transport instance. */ - public function getSwiftMailer() + public function getSymfonyTransport(): TransportInterface { - return $this->swift; + return $this->transport; } /** * Get the array of failed recipients. * * @return array + * @deprecated */ - public function failures() - { + public function failures(): array + { return $this->failedRecipients; } /** - * Set the Swift Mailer instance. - * - * @param \Swift_Mailer $swift - * @return void + * Set the Symfony Transport instance. */ - public function setSwiftMailer($swift) + public function setSymfonyTransport(TransportInterface $transport): void { - $this->swift = $swift; + $this->transport = $transport; } /** @@ -471,8 +436,8 @@ public function setSwiftMailer($swift) * @param \Illuminate\Log\Writer $logger * @return $this */ - public function setLogger(Writer $logger) - { + public function setLogger(Writer $logger): static + { $this->logger = $logger; return $this; @@ -484,8 +449,8 @@ public function setLogger(Writer $logger) * @param \Illuminate\Queue\QueueManager $queue * @return $this */ - public function setQueue(QueueManager $queue) - { + public function setQueue(QueueManager $queue): static + { $this->queue = $queue; return $this; @@ -497,8 +462,8 @@ public function setQueue(QueueManager $queue) * @param \Illuminate\Container\Container $container * @return void */ - public function setContainer(Container $container) - { + public function setContainer(Container $container): void + { $this->container = $container; } From 1d418fab4be2adbc791b99817acee5529dc9cd75 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 09:48:25 +0700 Subject: [PATCH 09/31] add array transport --- .../Mail/Transport/ArrayTransport.php | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/Illuminate/Mail/Transport/ArrayTransport.php diff --git a/src/Illuminate/Mail/Transport/ArrayTransport.php b/src/Illuminate/Mail/Transport/ArrayTransport.php new file mode 100644 index 00000000..9b0ccfce --- /dev/null +++ b/src/Illuminate/Mail/Transport/ArrayTransport.php @@ -0,0 +1,67 @@ +messages = new Collection(); + } + + /** + * {@inheritdoc} + */ + public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + { + return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message)); + } + + /** + * Retrieve the collection of messages. + * + * @return \Illuminate\Support\Collection + */ + public function messages(): Collection + { + return $this->messages; + } + + /** + * Clear all of the messages from the local collection. + * + * @return \Illuminate\Support\Collection + */ + public function flush(): Collection + { + return $this->messages = new Collection(); + } + + /** + * Get the string representation of the transport. + * + * @return string + */ + public function __toString(): string + { + return 'array'; + } +} \ No newline at end of file From 34cf2bf1bac86c4fe4a538dce125298822b7501b Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 10:26:52 +0700 Subject: [PATCH 10/31] fix addressing --- src/Illuminate/Mail/Message.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index 7ac90572..45d96508 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -37,7 +37,7 @@ public function __construct(Email $message) */ public function from(string $address, ?string $name = null): self { - $this->message->from(new Address($address, $name)); + $this->message->from(new Address($address, $name ?? '')); return $this; } @@ -70,7 +70,7 @@ public function returnPath(string $address): self /** * Add a recipient to the message. * - * @param string|array $address + * @param string|Address[] $address * @param ?string $name * @return $this */ @@ -118,21 +118,21 @@ public function replyTo(string $address, ?string $name = null): self /** * Add a recipient to the message. * - * @param string|array $address + * @param string|Address[] $address * @param string $name * @param string $type * @return $this */ - protected function addAddresses(string|array $address, string $name, string $type): self + protected function addAddresses(string|array $address, ?string $name, string $type): self { if (is_array($address)) { $type = lcfirst($type); - $this->message->$type($address, $name); + $this->message->$type($address); } else { - $this->message->{"add{$type}"}($address, $name); + $this->message->{"add{$type}"}(new Address($address, $name ?? '')); } return $this; From 2f220e9955767d7f165f0a6025b51367faf22197 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 10:35:32 +0700 Subject: [PATCH 11/31] fix some methods --- src/Illuminate/Mail/Mailer.php | 70 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index ac31a5ee..2c02744e 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -1,6 +1,7 @@ buildQueueCallable($callback); @@ -184,7 +189,7 @@ public function later(int $delay, $view, array $data, $callback, $queue = null): * @param \Closure|string $callback * @return mixed */ - public function laterOn($queue, $delay, $view, array $data, $callback): mixed + public function laterOn(string $queue, int $delay, string|array $view, array $data, Closure|string $callback): mixed { return $this->later($delay, $view, $data, $callback, $queue); } @@ -195,9 +200,11 @@ public function laterOn($queue, $delay, $view, array $data, $callback): mixed * @param mixed $callback * @return mixed */ - protected function buildQueueCallable($callback): mixed + protected function buildQueueCallable(mixed $callback): mixed { - if ( ! $callback instanceof Closure) return $callback; + if (!$callback instanceof Closure) { + return $callback; + } return serialize(new SerializableClosure($callback)); } @@ -209,10 +216,9 @@ protected function buildQueueCallable($callback): mixed * @param array $data * @return void */ - public function handleQueuedMessage($job, $data): void + public function handleQueuedMessage(Job $job, array $data): void { $this->send($data['view'], $data['data'], $this->getQueuedCallable($data)); - $job->delete(); } @@ -241,19 +247,24 @@ protected function getQueuedCallable(array $data): mixed * @param array $data * @return void */ - protected function addContent($message, $view, $plain, $data): void + protected function addContent(Message $message, ?string $view, ?string $plain, array $data): void { - if (isset($view)) + if ($view !== null) { - $message->setBody($this->getView($view, $data), 'text/html'); + $message->html($this->renderView($view, $data)); } - if (isset($plain)) + if ($plain !== null) { - $message->addPart($this->getView($plain, $data), 'text/plain'); + $message->text($this->renderView($plain, $data)); } } + protected function renderView(string $view, array $data): string + { + return $this->views->make($view, $data)->render(); + } + /** * Parse the given view name or array. * @@ -262,16 +273,18 @@ protected function addContent($message, $view, $plain, $data): void * * @throws \InvalidArgumentException */ - protected function parseView($view): array + protected function parseView(string|array $view): array { - if (is_string($view)) return array($view, null); + if (is_string($view)) { + return [$view, null]; + } // If the given view is an array with numeric keys, we will just assume that // both a "pretty" and "plain" view were provided, so we will return this // array as is, since must should contain both views with numeric keys. if (is_array($view) && isset($view[0])) { - return $view; + return [$view[0], $view[1] ?? null]; } // If the view is an array, but doesn't contain numeric keys, we will assume @@ -279,9 +292,10 @@ protected function parseView($view): array // named keys instead, allowing the developers to use one or the other. elseif (is_array($view)) { - return array( - array_get($view, 'html'), array_get($view, 'text') - ); + return [ + $view['html'] ?? null, + $view['text'] ?? null + ]; } throw new \InvalidArgumentException("Invalid view."); @@ -323,8 +337,6 @@ protected function logMessage(Email $message): void * @param string|\Closure $callback * @param \Illuminate\Mail\Message $message * @return mixed - * - * @throws \InvalidArgumentException */ protected function callMessageBuilder(string|Closure $callback, Message $message): mixed { @@ -332,12 +344,10 @@ protected function callMessageBuilder(string|Closure $callback, Message $message { return call_user_func($callback, $message); } - elseif (is_string($callback)) + else { return $this->container[$callback]->mail($message); } - - throw new \InvalidArgumentException("Callback is not valid."); } /** @@ -360,25 +370,13 @@ protected function createMessage(): Message return $message; } - /** - * Render the given view. - * - * @param string $view - * @param array $data - * @return \Illuminate\View\View - */ - protected function getView($view, $data) - { - return $this->views->make($view, $data)->render(); - } - /** * Tell the mailer to not really send messages. * * @param bool $value * @return void */ - public function pretend($value = true): void + public function pretend(bool $value = true): void { $this->pretending = $value; } From 8713edc93341f90f4d2867886982cbd128a09d63 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 10:36:04 +0700 Subject: [PATCH 12/31] fix test testMailerSendSendsMessageWithProperViewContent --- tests/Mail/MailMailerTest.php | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index 436d2cb0..10cfd487 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -2,10 +2,13 @@ use Illuminate\Log\Writer; use Illuminate\Mail\Mailer; +use Illuminate\Mail\Message; +use Illuminate\Mail\Transport\ArrayTransport; use Illuminate\Queue\QueueManager; use Illuminate\View\Factory; use L4\Tests\BackwardCompatibleTestCase; use Mockery as m; +use Symfony\Component\Mailer\SentMessage; class MailMailerTest extends BackwardCompatibleTestCase { @@ -15,23 +18,27 @@ protected function tearDown(): void m::close(); } - public function testMailerSendSendsMessageWithProperViewContent() { unset($_SERVER['__mailer.test']); - $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); - $message = m::mock('StdClass'); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock('StdClass'); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $mailer->setSwiftMailer(m::mock('StdClass')); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send('foo', ['data'], function($m) { $_SERVER['__mailer.test'] = $m; }); - unset($_SERVER['__mailer.test']); + + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + + $mailer = new Mailer($view, $transport = new ArrayTransport()); + $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); + }); + + $sentMessages = $transport->messages(); + self::assertCount(1, $sentMessages); + + /** @var SentMessage $sentMessage */ + $sentMessage = $sentMessages[0]; + self::assertStringContainsString('rendered.view', $sentMessage->toString()); + self::assertEquals('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); + self::assertEquals('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); } From 746ac9b2aee8ca7cf688dd968e8b99dcce6b7be3 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 16:43:45 +0700 Subject: [PATCH 13/31] refactor the MailMailerTest --- tests/Mail/MailMailerTest.php | 255 ++++++++++++++++++++-------------- 1 file changed, 151 insertions(+), 104 deletions(-) diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index 10cfd487..eea2af29 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -37,6 +37,7 @@ public function testMailerSendSendsMessageWithProperViewContent() /** @var SentMessage $sentMessage */ $sentMessage = $sentMessages[0]; self::assertStringContainsString('rendered.view', $sentMessage->toString()); + self::assertStringContainsString('Content-Type: text/html;', $sentMessage->toString()); self::assertEquals('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); self::assertEquals('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); } @@ -44,50 +45,85 @@ public function testMailerSendSendsMessageWithProperViewContent() public function testMailerSendSendsMessageWithProperPlainViewContent() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); - $message = m::mock('StdClass'); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock('StdClass'); - $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(); - $mailer->setSwiftMailer(m::mock('StdClass')); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['foo', 'bar'], ['data'], function($m) { $_SERVER['__mailer.test'] = $m; }); - unset($_SERVER['__mailer.test']); + $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($view, $transport = new ArrayTransport()); + $mailer->send(['foo', 'bar'], ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); + }); + + $sentMessages = $transport->messages(); + self::assertCount(1, $sentMessages); + + /** @var SentMessage $sentMessage */ + $sentMessage = $sentMessages[0]; + $expected = <<toString()); + + $expected = <<toString()); + self::assertStringContainsString('Content-Type: text/html;', $sentMessage->toString()); + self::assertEquals('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); + self::assertEquals('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); } public function testMailerSendSendsMessageWithProperPlainViewContentWhenExplicit() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); - $message = m::mock('StdClass'); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock('StdClass'); - $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(); - $mailer->setSwiftMailer(m::mock('StdClass')); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['html' => 'foo', 'text' => 'bar'], ['data'], function($m) { $_SERVER['__mailer.test'] = $m; }); - unset($_SERVER['__mailer.test']); + $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($view, $transport = new ArrayTransport()); + $mailer->send(['html' => 'foo', 'text' => 'bar'], ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); + }); + + $sentMessages = $transport->messages(); + self::assertCount(1, $sentMessages); + + /** @var SentMessage $sentMessage */ + $sentMessage = $sentMessages[0]; + $expected = <<toString()); + + $expected = <<toString()); } public function testMailerCanQueueMessagesToItself() { - list($view, $swift) = $this->getMocks(); - $mailer = new Illuminate\Mail\Mailer($view, $swift); + $view = m::mock(Factory::class); + $mailer = new Mailer($view, new ArrayTransport()); $mailer->setQueue($queue = m::mock(QueueManager::class)); $queue->shouldReceive('push')->once()->with('mailer@handleQueuedMessage', ['view' => 'foo', 'data' => [1], 'callback' => 'callable'], null); @@ -97,8 +133,8 @@ public function testMailerCanQueueMessagesToItself() public function testMailerCanQueueMessagesToItselfOnAnotherQueue() { - list($view, $swift) = $this->getMocks(); - $mailer = new Illuminate\Mail\Mailer($view, $swift); + $view = m::mock(Factory::class); + $mailer = new Mailer($view, new ArrayTransport()); $mailer->setQueue($queue = m::mock(QueueManager::class)); $queue->shouldReceive('push')->once()->with('mailer@handleQueuedMessage', ['view' => 'foo', 'data' => [1], 'callback' => 'callable'], 'queue'); @@ -108,8 +144,8 @@ public function testMailerCanQueueMessagesToItselfOnAnotherQueue() public function testMailerCanQueueMessagesToItselfWithSerializedClosures() { - list($view, $swift) = $this->getMocks(); - $mailer = new Illuminate\Mail\Mailer($view, $swift); + $view = m::mock(Factory::class); + $mailer = new Mailer($view, new ArrayTransport()); $mailer->setQueue($queue = m::mock(QueueManager::class)); $serialized = serialize(new Illuminate\Support\SerializableClosure($closure = function() {})); $queue->shouldReceive('push')->once()->with('mailer@handleQueuedMessage', ['view' => 'foo', 'data' => [1], 'callback' => $serialized], null); @@ -120,8 +156,8 @@ public function testMailerCanQueueMessagesToItselfWithSerializedClosures() public function testMailerCanQueueMessagesToItselfLater() { - list($view, $swift) = $this->getMocks(); - $mailer = new Illuminate\Mail\Mailer($view, $swift); + $view = m::mock(Factory::class); + $mailer = new Mailer($view, new ArrayTransport()); $mailer->setQueue($queue = m::mock(QueueManager::class)); $queue->shouldReceive('later')->once()->with(10, 'mailer@handleQueuedMessage', ['view' => 'foo', 'data' => [1], 'callback' => 'callable'], null); @@ -131,8 +167,8 @@ public function testMailerCanQueueMessagesToItselfLater() public function testMailerCanQueueMessagesToItselfLaterOnAnotherQueue() { - list($view, $swift) = $this->getMocks(); - $mailer = new Illuminate\Mail\Mailer($view, $swift); + $view = m::mock(Factory::class); + $mailer = new Mailer($view, new ArrayTransport()); $mailer->setQueue($queue = m::mock(QueueManager::class)); $queue->shouldReceive('later')->once()->with(10, 'mailer@handleQueuedMessage', ['view' => 'foo', 'data' => [1], 'callback' => 'callable'], 'queue'); @@ -142,83 +178,94 @@ public function testMailerCanQueueMessagesToItselfLaterOnAnotherQueue() public function testMessagesCanBeLoggedInsteadOfSent() { - $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); - $message = m::mock('StdClass'); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock('StdClass'); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $mailer->setSwiftMailer(m::mock('StdClass')); - $message->shouldReceive('getTo')->once()->andReturn(['taylor@userscape.com' => 'Taylor']); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->never(); - $logger = m::mock(Writer::class); - $logger->shouldReceive('info')->once()->with('Pretending to mail message to: taylor@userscape.com'); - $mailer->setLogger($logger); - $mailer->pretend(); - - $mailer->send('foo', ['data'], function($m) {}); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + + $mailer = new Mailer($view, $transport = new ArrayTransport()); + $logger = m::mock(Writer::class); + $logger->shouldReceive('info')->once()->with('Pretending to mail message to: taylor@userscape.com'); + $mailer->setLogger($logger); + $mailer->pretend(); + + $mailer->send('foo', ['data'], function (Message $message) { + $message->from('hello@laravel.com'); + $message->to('taylor@userscape.com'); + }); + + self::assertEmpty($transport->messages()); + +// $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); +// $message = m::mock('StdClass'); +// $mailer->expects($this->once())->method('createMessage')->willReturn($message); +// $view = m::mock('StdClass'); +// $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); +// $view->shouldReceive('render')->once()->andReturn('rendered.view'); +// $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); +// $message->shouldReceive('setFrom')->never(); +// $mailer->setSwiftMailer(m::mock('StdClass')); +// $message->shouldReceive('getTo')->once()->andReturn(['taylor@userscape.com' => 'Taylor']); +// $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); +// $mailer->getSwiftMailer()->shouldReceive('send')->never(); +// $logger = m::mock(Writer::class); +// $logger->shouldReceive('info')->once()->with('Pretending to mail message to: taylor@userscape.com'); +// $mailer->setLogger($logger); +// $mailer->pretend(); +// +// $mailer->send('foo', ['data'], function($m) {}); } public function testMailerCanResolveMailerClasses() { - $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); - $message = m::mock('StdClass'); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock('StdClass'); - $container = new Illuminate\Container\Container; - $mailer->setContainer($container); - $mockMailer = m::mock('StdClass'); - $container['FooMailer'] = $container->share(function() use ($mockMailer) - { - return $mockMailer; - }); - $mockMailer->shouldReceive('mail')->once()->with($message); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $mailer->setSwiftMailer(m::mock('StdClass')); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + + $mailer = new Mailer($view, $transport = new ArrayTransport()); + $container = new Illuminate\Container\Container(); + $mailer->setContainer($container); + $fooMailer = new class { + public int $calledTimes = 0; + public function mail(Message $message): void + { + $message->from('hello@laravel.com'); + $message->to('taylor@laravel.com'); + $this->calledTimes++; + } + }; + $container['FooMailer'] = $container->share(fn() => $fooMailer); + $mailer->send('foo', ['data'], 'FooMailer'); + + $sentMessage = $transport->messages()[0]; + self::assertEquals(1, $fooMailer->calledTimes); + self::assertEquals('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); + self::assertEquals('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); + } public function testGlobalFromIsRespectedOnAllMessages() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMailer(); - $view = m::mock('StdClass'); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $mailer->setSwiftMailer(m::mock('StdClass')); - $mailer->alwaysFrom('taylorotwell@gmail.com', 'Taylor Otwell'); - $me = $this; - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with(m::type('Swift_Message'), [])->andReturnUsing(function($message) use ($me) - { - $me->assertEquals(['taylorotwell@gmail.com' => 'Taylor Otwell'], $message->getFrom()); - }); - $mailer->send('foo', ['data'], function($m) {}); - } + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + $mailer = new Mailer($view, $transport = new ArrayTransport); + $mailer->alwaysFrom('hello@laravel.com'); + $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com'); + }); - public function testFailedRecipientsAreAppendedAndCanBeRetrieved() - { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMailer(); - $view = m::mock('StdClass'); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $swift = new FailingSwiftMailerStub; - $mailer->setSwiftMailer($swift); - $mailer->send('foo', ['data'], function($m) {}); + $sentMessages = $transport->messages(); + self::assertCount(1, $sentMessages); - $this->assertEquals(['taylorotwell@gmail.com'], $mailer->failures()); + /** @var SentMessage $sentMessage */ + $sentMessage = $sentMessages[0]; + self::assertSame('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); + self::assertSame('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); } From f933f5f7a0570d90d049d9f684378074d2fed4ce Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 17 Jul 2024 16:45:00 +0700 Subject: [PATCH 14/31] cleaning up --- tests/Mail/MailMailerTest.php | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index eea2af29..07a0c5a8 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -20,8 +20,6 @@ protected function tearDown(): void public function testMailerSendSendsMessageWithProperViewContent() { - unset($_SERVER['__mailer.test']); - $view = m::mock(Factory::class); $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); @@ -251,7 +249,7 @@ public function testGlobalFromIsRespectedOnAllMessages() $view = m::mock(Factory::class); $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $mailer = new Mailer($view, $transport = new ArrayTransport); + $mailer = new Mailer($view, $transport = new ArrayTransport()); $mailer->alwaysFrom('hello@laravel.com'); $mailer->send('foo', ['data'], function (Message $message) { @@ -267,25 +265,4 @@ public function testGlobalFromIsRespectedOnAllMessages() self::assertSame('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); self::assertSame('hello@laravel.com', $sentMessage->getEnvelope()->getSender()->getAddress()); } - - - protected function getMailer() - { - return new Illuminate\Mail\Mailer(m::mock(Factory::class), m::mock('Swift_Mailer')); - } - - - protected function getMocks() - { - return [m::mock(Factory::class), m::mock('Swift_Mailer')]; - } - -} - -class FailingSwiftMailerStub -{ - public function send($message, &$failed) - { - $failed[] = 'taylorotwell@gmail.com'; - } } From 1457ba41ed29227089f21596b939fff2f024f7d2 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 10:20:47 +0700 Subject: [PATCH 15/31] add symfony http client and mailgun mailer --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 3c2c560b..2eb8ef7a 100755 --- a/composer.json +++ b/composer.json @@ -29,9 +29,11 @@ "symfony/error-handler": "~6.4", "symfony/event-dispatcher": "~6.4", "symfony/finder": "~6.4", + "symfony/http-client": "^6.4", "symfony/http-foundation": "~6.4", "symfony/http-kernel": "~6.4", "symfony/mailer": "^6.4", + "symfony/mailgun-mailer": "^6.4", "symfony/mime": "~6.4", "symfony/process": "~6.4", "symfony/routing": "~6.4", From 33f4e84c8524f3e00b5325bb2420e39a59db5980 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 10:21:34 +0700 Subject: [PATCH 16/31] completely refactor the mail service provider --- src/Illuminate/Mail/MailServiceProvider.php | 252 ++++++++++---------- 1 file changed, 122 insertions(+), 130 deletions(-) diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 2598e3c9..3ae831cc 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -1,13 +1,17 @@ app->bindShared('mailer', function($app) use ($me) { - $me->registerSwiftMailer(); + $me->registerSymfonyMailer(); // Once we have create the mailer instance, we will set a container instance // on the mailer. This allows us to resolve mailer classes via containers // for maximum testability on said classes instead of passing Closures. $mailer = new Mailer( - $app['view'], $app['swift.mailer'], $app['events'] + $app['view'], $app['symfony.transport'], $app['events'] ); $this->setMailerDependencies($mailer, $app); @@ -68,7 +72,7 @@ public function register() * @param \Illuminate\Foundation\Application $app * @return void */ - protected function setMailerDependencies($mailer, $app) + protected function setMailerDependencies(Mailer $mailer, Application $app): void { $mailer->setContainer($app); @@ -83,179 +87,167 @@ protected function setMailerDependencies($mailer, $app) } } - /** - * Register the Swift Mailer instance. - * - * @return void - */ - public function registerSwiftMailer() - { - $config = $this->app['config']['mail']; - - $this->registerSwiftTransport($config); - - // Once we have the transporter registered, we will register the actual Swift - // mailer instance, passing in the transport instances, which allows us to - // override this transporter instances during app start-up if necessary. - $this->app['swift.mailer'] = $this->app->share(function($app) - { - return new Swift_Mailer($app['swift.transport']); - }); - } - - /** - * Register the Swift Transport instance. - * - * @param array $config - * @return void - * - * @throws \InvalidArgumentException - */ - protected function registerSwiftTransport($config) - { - switch ($config['driver']) - { - case 'smtp': - return $this->registerSmtpTransport($config); - - case 'sendmail': - return $this->registerSendmailTransport($config); - - case 'mail': - return $this->registerMailTransport($config); - - case 'mailgun': - return $this->registerMailgunTransport($config); - - case 'mandrill': - return $this->registerMandrillTransport($config); - - case 'log': - return $this->registerLogTransport($config); - - default: - throw new \InvalidArgumentException('Invalid mail driver.'); - } - } + public function registerSymfonyMailer(): void + { + $config = $this->app['config']['mail']; + + switch ($config['driver']) + { + case 'smtp': + $this->registerSmtpTransport($config); + break; + case 'sendmail': + $this->registerSendmailTransport($config); + break; + case 'mail': + $this->registerMailTransport($config); + break; + case 'mailgun': + $this->registerMailgunTransport($config); + break; +// case 'mandrill': +// $this->registerMandrillTransport($config); +// break; + case 'log': + $this->registerLogTransport($config); + break; + default: + throw new \InvalidArgumentException('Invalid mail driver.'); + } + } /** - * Register the SMTP Swift Transport instance. + * Register the SMTP symfony Transport instance. * * @param array $config * @return void */ - protected function registerSmtpTransport($config) + protected function registerSmtpTransport(array $config): void { - $this->app['swift.transport'] = $this->app->share(function($app) use ($config) + $this->app['symfony.transport'] = $this->app->share(function($app) use ($config) { - extract($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($host, $port); - - if (isset($encryption)) - { - $transport->setEncryption($encryption); - } - - // 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($username)) - { - $transport->setUsername($username); - - $transport->setPassword($password); - } + $factory = new EsmtpTransportFactory(); + + $scheme = $config['scheme'] ?? null; + + if (! $scheme) { + $scheme = ! empty($config['encryption']) && $config['encryption'] === 'tls' + ? (($config['port'] == 465) ? 'smtps' : 'smtp') + : ''; + } + + /** @var EsmtpTransport $transport */ + $transport = $factory->create(new Dsn( + $scheme, + $config['host'], + $config['username'] ?? null, + $config['password'] ?? null, + $config['port'] ?? null, + $config + )); + + $stream = $transport->getStream(); + + if ($stream instanceof SocketStream) { + if (isset($config['source_ip'])) { + $stream->setSourceIp($config['source_ip']); + } + + if (isset($config['timeout'])) { + $stream->setTimeout($config['timeout']); + } + } return $transport; }); } /** - * Register the Sendmail Swift Transport instance. + * Register the Sendmail Symfony Transport instance. * * @param array $config * @return void */ - protected function registerSendmailTransport($config) + protected function registerSendmailTransport(array $config): void { - $this->app['swift.transport'] = $this->app->share(function($app) use ($config) - { - return SendmailTransport::newInstance($config['sendmail']); - }); + $this->app['symfony.transport'] = $this->app->share(fn($app) => new SendmailTransport( + $config['path'] ?? $app['config']->get('mail.sendmail') + )); } /** - * Register the Mail Swift Transport instance. + * Register the Mail Symfony Transport instance. * * @param array $config * @return void */ - protected function registerMailTransport($config) + protected function registerMailTransport(array $config): void { - $this->app['swift.transport'] = $this->app->share(function() - { - return MailTransport::newInstance(); - }); + $this->app['symfony.transport'] = $this->app->share(fn() => new SendmailTransport()); } /** - * Register the Mailgun Swift Transport instance. + * Register the Mailgun Symfony Transport instance. * * @param array $config * @return void */ - protected function registerMailgunTransport($config) + protected function registerMailgunTransport(array $config): void { - $mailgun = $this->app['config']->get('services.mailgun', array()); - - $this->app->bindShared('swift.transport', function() use ($mailgun) + $this->app->bindShared('symfony.transport', function() use ($config) { - return new MailgunTransport($mailgun['secret'], $mailgun['domain']); + $factory = new MailgunTransportFactory(null, $this->getHttpClient($config)); + + if (! isset($config['secret'])) { + $config = $this->app['config']->get('services.mailgun', []); + } + + return $factory->create(new Dsn( + 'mailgun+'.($config['scheme'] ?? 'https'), + $config['endpoint'] ?? 'default', + $config['secret'], + $config['domain'] + )); }); } /** - * Register the Mandrill Swift Transport instance. + * Register the "Log" Symfony Transport instance. * * @param array $config * @return void */ - protected function registerMandrillTransport($config) + protected function registerLogTransport(array $config): void { - $mandrill = $this->app['config']->get('services.mandrill', array()); - - $this->app->bindShared('swift.transport', function() use ($mandrill) - { - return new MandrillTransport($mandrill['secret']); - }); + $this->app->bindShared('symfony.transport', fn($app) => new LogTransportV2($app->make('Psr\Log\LoggerInterface'))); } - /** - * Register the "Log" Swift Transport instance. - * - * @param array $config - * @return void - */ - protected function registerLogTransport($config) - { - $this->app->bindShared('swift.transport', function($app) - { - return new LogTransport($app->make('Psr\Log\LoggerInterface')); - }); - } + /** + * Get a configured Symfony HTTP client instance. + * + * @return \Symfony\Contracts\HttpClient\HttpClientInterface|null + */ + protected function getHttpClient(array $config): ?HttpClientInterface + { + $clientOptions = $config['client'] ?? false; + if ($clientOptions) { + $maxHostConnections = Arr::pull($clientOptions, 'max_host_connections', 6); + $maxPendingPushes = Arr::pull($clientOptions, 'max_pending_pushes', 50); + + return HttpClient::create($clientOptions, $maxHostConnections, $maxPendingPushes); + } + + return null; + } /** * Get the services provided by the provider. * * @return array */ - public function provides() + public function provides(): array { - return array('mailer', 'swift.mailer', 'swift.transport'); + return ['mailer', 'symfony.transport']; } } From 84370726068e3644455a31c0bd2d0ebbf2f146f3 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 10:39:56 +0700 Subject: [PATCH 17/31] remove the unused comments --- tests/Mail/MailMailerTest.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index 07a0c5a8..bb39609d 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -192,25 +192,6 @@ public function testMessagesCanBeLoggedInsteadOfSent() }); self::assertEmpty($transport->messages()); - -// $mailer = $this->getMock(Mailer::class, ['createMessage'], $this->getMocks()); -// $message = m::mock('StdClass'); -// $mailer->expects($this->once())->method('createMessage')->willReturn($message); -// $view = m::mock('StdClass'); -// $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); -// $view->shouldReceive('render')->once()->andReturn('rendered.view'); -// $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); -// $message->shouldReceive('setFrom')->never(); -// $mailer->setSwiftMailer(m::mock('StdClass')); -// $message->shouldReceive('getTo')->once()->andReturn(['taylor@userscape.com' => 'Taylor']); -// $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); -// $mailer->getSwiftMailer()->shouldReceive('send')->never(); -// $logger = m::mock(Writer::class); -// $logger->shouldReceive('info')->once()->with('Pretending to mail message to: taylor@userscape.com'); -// $mailer->setLogger($logger); -// $mailer->pretend(); -// -// $mailer->send('foo', ['data'], function($m) {}); } From ff8dbccfb09f5e5bc0a8ae979260b0aeb3fcf0b9 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 11:45:10 +0700 Subject: [PATCH 18/31] refactor failed test --- tests/Auth/AuthPasswordBrokerTest.php | 67 ++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/tests/Auth/AuthPasswordBrokerTest.php b/tests/Auth/AuthPasswordBrokerTest.php index 0f69eef7..fd9b1a8c 100755 --- a/tests/Auth/AuthPasswordBrokerTest.php +++ b/tests/Auth/AuthPasswordBrokerTest.php @@ -5,9 +5,12 @@ use Illuminate\Auth\Reminders\ReminderRepositoryInterface; use Illuminate\Auth\UserProviderInterface; use Illuminate\Mail\Mailer; +use Illuminate\Mail\Transport\ArrayTransport; +use Illuminate\View\Factory; use L4\Tests\BackwardCompatibleTestCase; use Mockery as m; use Prophecy\Prophecy\ObjectProphecy; +use Symfony\Component\Mailer\SentMessage; class AuthPasswordBrokerTest extends BackwardCompatibleTestCase { @@ -95,21 +98,35 @@ public function testBrokerCreatesReminderAndRedirectsWithoutError() public function testMailerIsCalledWithProperViewTokenAndCallback() { - unset($_SERVER['__auth.reminder']); - $broker = $this->getBroker($mocks = $this->getMocks()); - $callback = function($message, $user) { $_SERVER['__auth.reminder'] = true; }; - $user = m::mock(RemindableInterface::class); - $mocks['mailer']->shouldReceive('send')->once()->with('reminderView', ['token' => 'token', 'user' => $user], m::type('Closure'))->andReturnUsing(function($view, $data, $callback) - { - return $callback; - }); - $user->shouldReceive('getReminderEmail')->once()->andReturn('email'); - $message = m::mock('StdClass'); - $message->shouldReceive('to')->once()->with('email'); - $result = $broker->sendReminder($user, 'token', $callback); - call_user_func($result, $message); - - $this->assertTrue($_SERVER['__auth.reminder']); + $factoryView = m::mock(Factory::class); + $factoryView->shouldReceive('make')->once()->andReturnUsing(function ($view, $data) use($factoryView, &$maker) { + $factoryView->shouldReceive('render')->once()->andReturn($view); + return $factoryView; + }); + + $broker = $this->getBroker($mocks = [ + ...$this->getMocks(), + 'mailer' => new Mailer($factoryView, $transport = new ArrayTransport()) + ]); + $mocks['mailer']->alwaysFrom('sender@mail.com'); + + $user = m::mock(RemindableInterface::class); + $user->shouldReceive('getReminderEmail')->once()->andReturn('user@email.com'); + + $receivedCallback = new stdClass(); + $someCallback = function($message, $user, $token) use (&$receivedCallback) { + $receivedCallback->user = $user; + $receivedCallback->token = $token; + }; + + $broker->sendReminder($user, 'token', $someCallback); + + /** @var SentMessage $message */ + $message = $transport->messages()[0]; + self::assertEquals('user@email.com', $message->getEnvelope()->getRecipients()[0]->getAddress()); + self::assertStringContainsString('reminderView', $message->toString()); + self::assertEquals($user, $receivedCallback->user); + self::assertEquals('token', $receivedCallback->token); } @@ -207,22 +224,20 @@ public function testResetRemovesRecordOnReminderTableAndCallsCallback() } - protected function getBroker($mocks) - { + protected function getBroker($mocks): PasswordBroker + { return new PasswordBroker($mocks['reminders'], $mocks['users'], $mocks['mailer'], $mocks['view']); } - protected function getMocks() - { - $mocks = [ - 'reminders' => m::mock(ReminderRepositoryInterface::class), - 'users' => m::mock(UserProviderInterface::class), - 'mailer' => m::mock(Mailer::class), - 'view' => 'reminderView', + protected function getMocks(): array + { + return [ + 'reminders' => m::mock(ReminderRepositoryInterface::class), + 'users' => m::mock(UserProviderInterface::class), + 'mailer' => m::mock(Mailer::class), + 'view' => 'reminderView', ]; - - return $mocks; } } From 6626cba0729154533c2f11d1f2b1b2a014ffd2a2 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 11:49:37 +0700 Subject: [PATCH 19/31] refactor password broker --- .../Auth/Reminders/PasswordBroker.php | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/Illuminate/Auth/Reminders/PasswordBroker.php b/src/Illuminate/Auth/Reminders/PasswordBroker.php index 6b914b46..86d14f6c 100755 --- a/src/Illuminate/Auth/Reminders/PasswordBroker.php +++ b/src/Illuminate/Auth/Reminders/PasswordBroker.php @@ -46,35 +46,35 @@ class PasswordBroker { * * @var \Illuminate\Auth\Reminders\ReminderRepositoryInterface $reminders */ - protected $reminders; + protected ReminderRepositoryInterface $reminders; /** * The user provider implementation. * * @var \Illuminate\Auth\UserProviderInterface */ - protected $users; + protected UserProviderInterface $users; /** * The mailer instance. * * @var \Illuminate\Mail\Mailer */ - protected $mailer; + protected Mailer $mailer; /** * The view of the password reminder e-mail. * * @var string */ - protected $reminderView; + protected string $reminderView; /** * The custom password validator callback. * * @var \Closure */ - protected $passwordValidator; + protected Closure $passwordValidator; /** * Create a new password broker instance. @@ -82,13 +82,13 @@ class PasswordBroker { * @param \Illuminate\Auth\Reminders\ReminderRepositoryInterface $reminders * @param \Illuminate\Auth\UserProviderInterface $users * @param \Illuminate\Mail\Mailer $mailer - * @param string $reminderView + * @param string $reminderView * @return void */ public function __construct(ReminderRepositoryInterface $reminders, - UserProviderInterface $users, - Mailer $mailer, - $reminderView) + UserProviderInterface $users, + Mailer $mailer, + string $reminderView) { $this->users = $users; $this->mailer = $mailer; @@ -103,14 +103,14 @@ public function __construct(ReminderRepositoryInterface $reminders, * @param \Closure $callback * @return string */ - public function remind(array $credentials, Closure $callback = null) + public function remind(array $credentials, Closure $callback = null): string { // First we will check to see if we found a user at the given credentials and // if we did not we will redirect back to this current URI with a piece of // "flash" data in the session to indicate to the developers the errors. $user = $this->getUser($credentials); - if (is_null($user)) + if ($user === null) { return self::INVALID_USER; } @@ -129,22 +129,24 @@ public function remind(array $credentials, Closure $callback = null) * Send the password reminder e-mail. * * @param \Illuminate\Auth\Reminders\RemindableInterface $user - * @param string $token - * @param \Closure $callback - * @return int + * @param string $token + * @param Closure $callback + * @return void */ - public function sendReminder(RemindableInterface $user, $token, Closure $callback = null) + public function sendReminder(RemindableInterface $user, string $token, Closure $callback = null): void { // We will use the reminder view that was given to the broker to display the // password reminder e-mail. We'll pass a "token" variable into the views // so that it may be displayed for an user to click for password reset. $view = $this->reminderView; - return $this->mailer->send($view, compact('token', 'user'), function($m) use ($user, $token, $callback) + $this->mailer->send($view, compact('token', 'user'), function($m) use ($user, $token, $callback) { $m->to($user->getReminderEmail()); - if ( ! is_null($callback)) call_user_func($callback, $m, $user, $token); + if ($callback !== null) { + call_user_func($callback, $m, $user, $token); + } }); } @@ -153,10 +155,10 @@ public function sendReminder(RemindableInterface $user, $token, Closure $callbac * * @param array $credentials * @param \Closure $callback - * @return mixed - */ - public function reset(array $credentials, Closure $callback) - { + * @return RemindableInterface|string|int + */ + public function reset(array $credentials, Closure $callback): RemindableInterface|string|int + { // If the responses from the validate method is not a user instance, we will // assume that it is a redirect and simply return it from this method and // the user is properly redirected having an error message on the post. @@ -179,25 +181,26 @@ public function reset(array $credentials, Closure $callback) return self::PASSWORD_RESET; } - /** - * Validate a password reset for the given credentials. - * - * @param array $credentials - * @return \Illuminate\Auth\Reminders\RemindableInterface - */ - protected function validateReset(array $credentials) - { - if (is_null($user = $this->getUser($credentials))) + /** + * Validate a password reset for the given credentials. + * + * @param array $credentials + * @return RemindableInterface|int|string + */ + protected function validateReset(array $credentials): RemindableInterface|int|string + { + $user = $this->getUser($credentials); + if ($user === null) { return self::INVALID_USER; } - if ( ! $this->validNewPasswords($credentials)) + if (!$this->validNewPasswords($credentials)) { return self::INVALID_PASSWORD; } - if ( ! $this->reminders->exists($user, $credentials['token'])) + if (!$this->reminders->exists($user, $credentials['token'])) { return self::INVALID_TOKEN; } @@ -211,8 +214,8 @@ protected function validateReset(array $credentials) * @param \Closure $callback * @return void */ - public function validator(Closure $callback) - { + public function validator(Closure $callback): void + { $this->passwordValidator = $callback; } @@ -222,9 +225,9 @@ public function validator(Closure $callback) * @param array $credentials * @return bool */ - protected function validNewPasswords(array $credentials) - { - list($password, $confirm) = array($credentials['password'], $credentials['password_confirmation']); + protected function validNewPasswords(array $credentials): bool + { + list($password, $confirm) = [$credentials['password'], $credentials['password_confirmation']]; if (isset($this->passwordValidator)) { @@ -240,24 +243,23 @@ protected function validNewPasswords(array $credentials) * @param array $credentials * @return bool */ - protected function validatePasswordWithDefaults(array $credentials) - { + protected function validatePasswordWithDefaults(array $credentials): bool + { list($password, $confirm) = [$credentials['password'], $credentials['password_confirmation']]; return $password === $confirm && mb_strlen((string) $password) >= 6; } - /** - * Get the user for the given credentials. - * - * @param array $credentials - * @return \Illuminate\Auth\Reminders\RemindableInterface - * - * @throws \UnexpectedValueException - */ - public function getUser(array $credentials) - { - $credentials = array_except($credentials, array('token')); + /** + * Get the user for the given credentials. + * + * @param array $credentials + * @return RemindableInterface|null + * + */ + public function getUser(array $credentials): ?RemindableInterface + { + $credentials = array_except($credentials, ['token']); $user = $this->users->retrieveByCredentials($credentials); @@ -274,8 +276,8 @@ public function getUser(array $credentials) * * @return \Illuminate\Auth\Reminders\ReminderRepositoryInterface */ - protected function getRepository() - { + protected function getRepository(): ReminderRepositoryInterface + { return $this->reminders; } From 98df200aed5801946934494a4ddf7a935bfec20d Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 12:13:36 +0700 Subject: [PATCH 20/31] remove temporary --- composer.json | 2 - src/Illuminate/Mail/MailServiceProvider.php | 82 ++++++++++----------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/composer.json b/composer.json index 2eb8ef7a..3c2c560b 100755 --- a/composer.json +++ b/composer.json @@ -29,11 +29,9 @@ "symfony/error-handler": "~6.4", "symfony/event-dispatcher": "~6.4", "symfony/finder": "~6.4", - "symfony/http-client": "^6.4", "symfony/http-foundation": "~6.4", "symfony/http-kernel": "~6.4", "symfony/mailer": "^6.4", - "symfony/mailgun-mailer": "^6.4", "symfony/mime": "~6.4", "symfony/process": "~6.4", "symfony/routing": "~6.4", diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 3ae831cc..89a58328 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -4,14 +4,14 @@ use Illuminate\Mail\Transport\LogTransportV2; use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; -use Symfony\Component\HttpClient\HttpClient; -use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; +//use Symfony\Component\HttpClient\HttpClient; +//use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Transport\Dsn; 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; -use Symfony\Contracts\HttpClient\HttpClientInterface; +//use Symfony\Contracts\HttpClient\HttpClientInterface; class MailServiceProvider extends ServiceProvider { @@ -102,9 +102,9 @@ public function registerSymfonyMailer(): void case 'mail': $this->registerMailTransport($config); break; - case 'mailgun': - $this->registerMailgunTransport($config); - break; +// case 'mailgun': +// $this->registerMailgunTransport($config); +// break; // case 'mandrill': // $this->registerMandrillTransport($config); // break; @@ -192,24 +192,24 @@ protected function registerMailTransport(array $config): void * @param array $config * @return void */ - protected function registerMailgunTransport(array $config): void - { - $this->app->bindShared('symfony.transport', function() use ($config) - { - $factory = new MailgunTransportFactory(null, $this->getHttpClient($config)); - - if (! isset($config['secret'])) { - $config = $this->app['config']->get('services.mailgun', []); - } - - return $factory->create(new Dsn( - 'mailgun+'.($config['scheme'] ?? 'https'), - $config['endpoint'] ?? 'default', - $config['secret'], - $config['domain'] - )); - }); - } +// protected function registerMailgunTransport(array $config): void +// { +// $this->app->bindShared('symfony.transport', function() use ($config) +// { +// $factory = new MailgunTransportFactory(null, $this->getHttpClient($config)); +// +// if (! isset($config['secret'])) { +// $config = $this->app['config']->get('services.mailgun', []); +// } +// +// return $factory->create(new Dsn( +// 'mailgun+'.($config['scheme'] ?? 'https'), +// $config['endpoint'] ?? 'default', +// $config['secret'], +// $config['domain'] +// )); +// }); +// } /** * Register the "Log" Symfony Transport instance. @@ -222,23 +222,23 @@ protected function registerLogTransport(array $config): void $this->app->bindShared('symfony.transport', fn($app) => new LogTransportV2($app->make('Psr\Log\LoggerInterface'))); } - /** - * Get a configured Symfony HTTP client instance. - * - * @return \Symfony\Contracts\HttpClient\HttpClientInterface|null - */ - protected function getHttpClient(array $config): ?HttpClientInterface - { - $clientOptions = $config['client'] ?? false; - if ($clientOptions) { - $maxHostConnections = Arr::pull($clientOptions, 'max_host_connections', 6); - $maxPendingPushes = Arr::pull($clientOptions, 'max_pending_pushes', 50); - - return HttpClient::create($clientOptions, $maxHostConnections, $maxPendingPushes); - } - - return null; - } +// /** +// * Get a configured Symfony HTTP client instance. +// * +// * @return \Symfony\Contracts\HttpClient\HttpClientInterface|null +// */ +// protected function getHttpClient(array $config): ?HttpClientInterface +// { +// $clientOptions = $config['client'] ?? false; +// if ($clientOptions) { +// $maxHostConnections = Arr::pull($clientOptions, 'max_host_connections', 6); +// $maxPendingPushes = Arr::pull($clientOptions, 'max_pending_pushes', 50); +// +// return HttpClient::create($clientOptions, $maxHostConnections, $maxPendingPushes); +// } +// +// return null; +// } /** * Get the services provided by the provider. From 7da8272bf9b367f52a00ee774c2233f61a266181 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 15:46:10 +0700 Subject: [PATCH 21/31] add test from laravel 9.x --- tests/Mail/MailMessageTest.php | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/Mail/MailMessageTest.php b/tests/Mail/MailMessageTest.php index c03480f1..35ef8228 100755 --- a/tests/Mail/MailMessageTest.php +++ b/tests/Mail/MailMessageTest.php @@ -3,6 +3,7 @@ use Illuminate\Mail\Message; use L4\Tests\BackwardCompatibleTestCase; use Mockery as m; +use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; class MailMessageTest extends BackwardCompatibleTestCase @@ -34,6 +35,62 @@ protected function tearDown(): void m::close(); } + public function testFromMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->from('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getFrom()[0]); + } + + public function testSenderMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->sender('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getSender()); + } + + public function testReturnPathMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->returnPath('foo@bar.baz')); + self::assertEquals(new Address('foo@bar.baz'), $message->getSymfonyMessage()->getReturnPath()); + } + + public function testToMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->to('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getTo()[0]); + + self::assertInstanceOf(Message::class, $message = $this->message->to(['bar@bar.baz' => 'Bar'])); + self::assertEquals(new Address('bar@bar.baz', 'Bar'), $message->getSymfonyMessage()->getTo()[0]); + } + + public function testCcMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->cc('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getCc()[0]); + } + + public function testBccMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->bcc('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getBcc()[0]); + } + + public function testReplyToMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->replyTo('foo@bar.baz', 'Foo')); + self::assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getReplyTo()[0]); + } + + public function testSubjectMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->subject('foo')); + self::assertSame('foo', $message->getSymfonyMessage()->getSubject()); + } + + public function testPriorityMethod() + { + self::assertInstanceOf(Message::class, $message = $this->message->priority(1)); + self::assertEquals(1, $message->getSymfonyMessage()->getPriority()); + } public function testBasicAttachment(): void { From e4f160b7b06b9a1430b3959ec1a523f2fc0d7fa6 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 18 Jul 2024 15:46:15 +0700 Subject: [PATCH 22/31] pass test --- src/Illuminate/Mail/Message.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index 45d96508..c40e3f55 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -1,5 +1,6 @@ $address * @param string $name * @param string $type * @return $this @@ -128,7 +129,22 @@ protected function addAddresses(string|array $address, ?string $name, string $ty if (is_array($address)) { $type = lcfirst($type); - $this->message->$type($address); + $addresses = (new Collection($address))->map(function ($address, $key) { + if (is_string($key) && is_string($address)) { + return new Address($key, $address); + } + + if (is_array($address)) { + return new Address($address['email'] ?? $address['address'], $address['name'] ?? null); + } + + if (is_null($address)) { + return new Address($key); + } + + return $address; + })->all(); + $this->message->$type(...$addresses); } else { From def812f55fae46c2266ce963532272dfcb4d163e Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 10 Jun 2025 16:39:37 +0700 Subject: [PATCH 23/31] remove swiftmailer --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 3c2c560b..6fd1f052 100755 --- a/composer.json +++ b/composer.json @@ -21,7 +21,6 @@ "pda/pheanstalk": "~4.0", "phpseclib/phpseclib": "~2.0", "predis/predis": "^1.1", - "swiftmailer/swiftmailer": "^6.0", "symfony/browser-kit": "~6.4", "symfony/console": "~6.4", "symfony/css-selector": "~6.4", From 93324af7fddfd77796e2d5269b793172e5a32b12 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Tue, 10 Jun 2025 16:47:40 +0700 Subject: [PATCH 24/31] add optional for safe null --- src/Illuminate/Mail/Message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index c40e3f55..714a6e05 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -51,7 +51,7 @@ public function from(string $address, ?string $name = null): self */ public function sender(string $address, ?string $name = null): self { - $this->message->sender(new Address($address, $name)); + $this->message->sender(new Address($address, $name ?? '')); return $this; } From 2f65f08db8d14f69c039cd8850b19fbf3c6e31bf Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Wed, 25 Jun 2025 16:12:54 +0700 Subject: [PATCH 25/31] revert to return just $view as is --- src/Illuminate/Mail/Mailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index 2c02744e..c4744096 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -284,7 +284,7 @@ protected function parseView(string|array $view): array // array as is, since must should contain both views with numeric keys. if (is_array($view) && isset($view[0])) { - return [$view[0], $view[1] ?? null]; + return $view; } // If the view is an array, but doesn't contain numeric keys, we will assume From f48d9b7a09c1cf9d18f49a7dd2d2b3a9ae39abdd Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 14:36:56 +0700 Subject: [PATCH 26/31] remove the unused transport --- .../Mail/Transport/MailgunTransport.php | 168 ------------------ .../Mail/Transport/MandrillTransport.php | 107 ----------- 2 files changed, 275 deletions(-) delete mode 100644 src/Illuminate/Mail/Transport/MailgunTransport.php delete mode 100644 src/Illuminate/Mail/Transport/MandrillTransport.php diff --git a/src/Illuminate/Mail/Transport/MailgunTransport.php b/src/Illuminate/Mail/Transport/MailgunTransport.php deleted file mode 100644 index 3b19f88e..00000000 --- a/src/Illuminate/Mail/Transport/MailgunTransport.php +++ /dev/null @@ -1,168 +0,0 @@ -key = $key; - $this->setDomain($domain); - } - - /** - * {@inheritdoc} - */ - public function isStarted() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function start() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function stop() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) - { - $client = $this->getHttpClient(); - - $client->post($this->url, ['auth' => ['api', $this->key], - 'body' => [ - 'to' => $this->getTo($message), - 'message' => new PostFile('message', (string) $message), - ], - ]); - } - - /** - * {@inheritdoc} - */ - public function registerPlugin(Swift_Events_EventListener $plugin) - { - // - } - - /** - * Get the "to" payload field for the API request. - * - * @param \Swift_Mime_Message $message - * @return array - */ - protected function getTo(Swift_Mime_Message $message) - { - $formatted = []; - - $contacts = array_merge( - (array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc() - ); - - foreach ($contacts as $address => $display) - { - $formatted[] = $display ? $display." <$address>" : $address; - } - - return implode(',', $formatted); - } - - /** - * Get a new HTTP client instance. - * - * @return \GuzzleHttp\Client - */ - protected function getHttpClient() - { - return new Client; - } - - /** - * 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 void - */ - 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 void - */ - public function setDomain($domain) - { - $this->url = 'https://api.mailgun.net/v2/'.$domain.'/messages.mime'; - - return $this->domain = $domain; - } - -} diff --git a/src/Illuminate/Mail/Transport/MandrillTransport.php b/src/Illuminate/Mail/Transport/MandrillTransport.php deleted file mode 100644 index daa823b6..00000000 --- a/src/Illuminate/Mail/Transport/MandrillTransport.php +++ /dev/null @@ -1,107 +0,0 @@ -key = $key; - } - - /** - * {@inheritdoc} - */ - public function isStarted() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function start() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function stop() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) - { - $client = $this->getHttpClient(); - - $client->post('https://mandrillapp.com/api/1.0/messages/send-raw.json', [ - 'body' => [ - 'key' => $this->key, - 'raw_message' => (string) $message, - 'async' => false, - ], - ]); - } - - /** - * {@inheritdoc} - */ - public function registerPlugin(Swift_Events_EventListener $plugin) - { - // - } - - /** - * Get a new HTTP client instance. - * - * @return \GuzzleHttp\Client - */ - protected function getHttpClient() - { - return new Client; - } - - /** - * 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 void - */ - public function setKey($key) - { - return $this->key = $key; - } - -} From 73d3c7f5a51cc4df61712ac8497ffea15f5395c8 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 14:38:00 +0700 Subject: [PATCH 27/31] remove the unused transport --- .../Mail/Transport/LogTransport.php | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 src/Illuminate/Mail/Transport/LogTransport.php diff --git a/src/Illuminate/Mail/Transport/LogTransport.php b/src/Illuminate/Mail/Transport/LogTransport.php deleted file mode 100644 index 1fe81360..00000000 --- a/src/Illuminate/Mail/Transport/LogTransport.php +++ /dev/null @@ -1,87 +0,0 @@ -logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function isStarted() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function start() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function stop() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_Message $message, &$failedRecipients = null) - { - $this->logger->debug($this->getMimeEntityString($message)); - } - - /** - * Get a loggable string out of a Swiftmailer entity. - * - * @param \Swift_Mime_MimeEntity $entity - * @return string - */ - protected function getMimeEntityString(Swift_Mime_MimeEntity $entity) - { - $string = (string) $entity->getHeaders().PHP_EOL.$entity->getBody(); - - foreach ($entity->getChildren() as $children) - { - $string .= PHP_EOL.PHP_EOL.$this->getMimeEntityString($children); - } - - return $string; - } - - /** - * {@inheritdoc} - */ - public function registerPlugin(Swift_Events_EventListener $plugin) - { - // - } - -} From 2bb075cfdd2e7fbb181c380209c12133e5ce60b0 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 14:38:20 +0700 Subject: [PATCH 28/31] migrate back to LogTransport --- src/Illuminate/Mail/MailServiceProvider.php | 4 ++-- .../Mail/Transport/{LogTransportV2.php => LogTransport.php} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Illuminate/Mail/Transport/{LogTransportV2.php => LogTransport.php} (94%) diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 89a58328..6c1f4d28 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -1,7 +1,7 @@ app->bindShared('symfony.transport', fn($app) => new LogTransportV2($app->make('Psr\Log\LoggerInterface'))); + $this->app->bindShared('symfony.transport', fn($app) => new LogTransport($app->make('Psr\Log\LoggerInterface'))); } // /** diff --git a/src/Illuminate/Mail/Transport/LogTransportV2.php b/src/Illuminate/Mail/Transport/LogTransport.php similarity index 94% rename from src/Illuminate/Mail/Transport/LogTransportV2.php rename to src/Illuminate/Mail/Transport/LogTransport.php index 9d85d23a..97bd56f9 100644 --- a/src/Illuminate/Mail/Transport/LogTransportV2.php +++ b/src/Illuminate/Mail/Transport/LogTransport.php @@ -8,7 +8,7 @@ use Symfony\Component\Mailer\Transport\TransportInterface; use Symfony\Component\Mime\RawMessage; -class LogTransportV2 implements TransportInterface +class LogTransport implements TransportInterface { protected LoggerInterface $logger; From 46606fe22eb8558eef88935c23b3150da1a933be Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 14:38:36 +0700 Subject: [PATCH 29/31] remove unused imports --- src/Illuminate/Mail/MailServiceProvider.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 6c1f4d28..e9be1f94 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -2,16 +2,12 @@ use Illuminate\Foundation\Application; use Illuminate\Mail\Transport\LogTransport; -use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; -//use Symfony\Component\HttpClient\HttpClient; -//use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Transport\Dsn; 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; -//use Symfony\Contracts\HttpClient\HttpClientInterface; class MailServiceProvider extends ServiceProvider { From 87e4a3b9998c32c828a24d46d15ccb8604f2f426 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 14:43:37 +0700 Subject: [PATCH 30/31] put back throws --- src/Illuminate/Auth/Reminders/PasswordBroker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Auth/Reminders/PasswordBroker.php b/src/Illuminate/Auth/Reminders/PasswordBroker.php index 86d14f6c..979163f8 100755 --- a/src/Illuminate/Auth/Reminders/PasswordBroker.php +++ b/src/Illuminate/Auth/Reminders/PasswordBroker.php @@ -255,7 +255,7 @@ protected function validatePasswordWithDefaults(array $credentials): bool * * @param array $credentials * @return RemindableInterface|null - * + * @throws \UnexpectedValueException */ public function getUser(array $credentials): ?RemindableInterface { From f8645d606cf07093c850377aa7b071c4f1605f62 Mon Sep 17 00:00:00 2001 From: AlexzPurewoko Date: Thu, 26 Jun 2025 15:49:20 +0700 Subject: [PATCH 31/31] remove swiftmailer --- src/Illuminate/Mail/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index 71e648da..33715802 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -13,7 +13,6 @@ "illuminate/log": "4.2.*", "illuminate/support": "4.2.*", "illuminate/view": "4.2.*", - "swiftmailer/swiftmailer": "^6.0", "symfony/mailer": "^6.4" }, "require-dev": {