当我使用 Laravel 的邮件发送功能时,脑子里浮现出这么几个问题:
> 1. Laravel 集成了 SMTP 、Mailgun 、SparkPost 、 Amazon SES 等驱动,是怎么做到的?
>
> 2. Laravel 提供全文本格式、网页格式和 Markdown 格式,是怎么实现的?
>
> 3. 整个邮件发送流程是什么样的?
>
下面就让我们开始徒手扒一扒「邮件发送功能」的实现原理。
## 写个 demo
我们使用阿里云提供的免费邮,和采用「smtp」驱动,作为测试,参考 `.env` 配置:
```
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mxhichina.com
MAIL_PORT=25
MAIL_USERNAME=***@coding01.cn
MAIL_PASSWORD=****
MAIL_ENCRYPTION=tls
MAIL_FROM=***@coding01.cn
MAIL_NAME=coding01
```
写个测试流程,还是挺简单的,具体如下:
```bash
// 1. 创建测试类
php artisan make:mail TestEmail
// 2. 在 TestEmail 类,载入视图
public function build()
{
return $this->view('mail.test');
}
// 3. 输出 hello coding01
<p>hello coding01</p>
```
最后写个命令函数:
```php
Artisan::command('test', function () {
Mail::to('yemeishu@126.com')->send(new \App\Mail\TestEmail());
});
```
执行 `php artisan test` 看测试是否发送成功:
![](https://user-gold-cdn.xitu.io/2018/8/26/165758aba593d21d?w=326&h=186&f=jpeg&s=28649)
## 解析 MailServiceProvider
写了不少 Laravel 代码,看
```php
Mail::to('yemeishu@126.com')->send(new \App\Mail\TestEmail());
```
自然而然的想到是不是有一个 `MailServiceProvider`,果不其然,在 `config/app.php` 的数组 `providers` 就包含了该 `ServiceProvider`
![](https://user-gold-cdn.xitu.io/2018/8/26/165758aba6f594bb?w=774&h=212&f=jpeg&s=99723)
所以我们就开始围绕这个 `MailServiceProvider` 来解析了
```php
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerSwiftMailer();
$this->registerIlluminateMailer();
$this->registerMarkdownRenderer();
}
```
看 `register` 函数,一目了然,我们将重点看看这三个方法都是干嘛用的。
### registerSwiftMailer
看代码:
```php
/**
* Register the Swift Mailer instance.
*
* @return void
*/
public function registerSwiftMailer()
{
$this->registerSwiftTransport();
// 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->singleton('swift.mailer', function ($app) {
if ($domain = $app->make('config')->get('mail.domain')) {
Swift_DependencyContainer::getInstance()
->register('mime.idgenerator.idright')
->asValue($domain);
}
return new Swift_Mailer($app['swift.transport']->driver());
});
}
```
很好理解,就是注册 `Swift Mailer` 实例。在创建实例之前,执行 `$this->registerSwiftTransport();`方法:
```php
/**
* Register the Swift Transport instance.
*
* @return void
*/
protected function registerSwiftTransport()
{
$this->app->singleton('swift.transport', function ($app) {
return new TransportManager($app);
});
}
```
看看这个 `TransportManager` 类是干嘛用的:
```php
<?php
namespace Illuminate\Mail;
use Aws\Ses\SesClient;
use Illuminate\Support\Arr;
use Psr\Log\LoggerInterface;
use Illuminate\Support\Manager;
use GuzzleHttp\Client as HttpClient;
use Swift_SmtpTransport as SmtpTransport;
use Illuminate\Mail\Transport\LogTransport;
use Illuminate\Mail\Transport\SesTransport;