tobento / service-mail
PHP 应用程序的邮件接口。
Requires
- php: >=8.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/http-message: ^1.0
- symfony/mailer: ^6.0
- tijsverkoyen/css-to-inline-styles: ^2.2.5
- tobento/service-autowire: ^1.0
- tobento/service-filesystem: ^1.0.5
- tobento/service-view: ^1.0.7
Requires (Dev)
- nyholm/psr7: ^1.4
- phpunit/phpunit: ^9.5
- tobento/service-collection: ^1.0
- tobento/service-container: ^1.0
- tobento/service-event: ^1.0
- tobento/service-queue: ^1.0
- vimeo/psalm: ^4.0
Suggests
- tobento/service-queue: May be used to support queuing messages
README
使用 Symfony Mailer 作为默认邮件实现方式的 PHP 应用程序邮件接口。
目录
入门
使用以下命令添加运行中的邮件服务项目最新版本。
composer require tobento/service-mail
要求
- PHP 8.0 或更高版本
亮点
- 框架无关,适用于任何项目
- 解耦设计
文档
基本用法
创建和发送消息
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\Message; class SomeService { public function send(MailerInterface $mailer): void { $message = (new Message()) ->from('from@example.com') ->to('to@example.com') //->cc('cc@example.com') //->bcc('bcc@example.com') //->replyTo('replyto@example.com') ->subject('Subject') //->textTemplate('welcome-text') //->htmlTemplate('welcome') //->text('Lorem Ipsum') ->html('<p>Lorem Ipsum</p>'); $mailer->send($message); } }
查看可用的 邮件发送器。
查看 消息 了解更多。
消息
电子邮件地址
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Address; $message = (new Message()) // email address as a simple string: ->from('from@example.com') // email address and name (optional) as object: ->from(new Address('from@example.com', 'Name')) ->replyTo('replyto@example.com') // the following methods support multiple addresses // as strings or objects: ->to('to@example.com', 'anotherTo@example.com') ->to( new Address('to@example.com'), new Address('anotherTo@example.com') ) ->cc('cc@example.com', new Address('anotherCc@example.com')) ->bcc('bcc@example.com', new Address('anotherBcc@example.com'));
内容
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Template; $message = (new Message()) // content defined as a string: ->subject('Subject') ->text('Lorem Ipsum') ->html('<p>Lorem Ipsum</p>') // content defined with a template object: ->text(new Template( name: 'welcome-text', data: ['name' => 'John'], )) ->html(new Template('welcome', [])) // using template methods: ->textTemplate(name: 'welcome-text', data: []) ->htmlTemplate('welcome', []);
标题
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Parameter; use Tobento\Service\Mail\Address; $message = (new Message()) // Text header: ->parameter(new Parameter\TextHeader( name: 'X-Custom-Header', value: 'value', )) // Id header: ->parameter(new Parameter\IdHeader( name: 'References', ids: ['a@example.com', 'b@example.com'], )) // Path header: ->parameter(new Parameter\PathHeader( name: 'Return-Path', address: 'return@example.com', // or as object // address: new Address('return@example.com'), ));
文件附件
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Parameter; use Tobento\Service\Filesystem\File; use Psr\Http\Message\StreamInterface; $message = (new Message()) // File defined as string: ->parameter(new Parameter\File( file: '/path/to/document.pdf', // optional parameters: filename: 'Document', mimeType: 'application/pdf', )) // File defined with File object: ->parameter(new Parameter\File( file: new File('/path/to/document.pdf'), )) // StreamFile: ->parameter(new Parameter\StreamFile( stream: $stream, // StreamInterface filename: 'Filename.png', // optional parameters: mimeType: 'image/png', )) // ResourceFile: ->parameter(new Parameter\ResourceFile( resource: fopen('/path/to/image.png', 'r+'), filename: 'Image.png', // optional parameters: mimeType: 'image/png', ));
标签和元数据
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Parameter; $message = (new Message()) // Tags: ->parameter(new Parameter\Tags(['tagname'])) // Metadata: ->parameter(new Parameter\Metadata([ 'name' => 'value', ]));
队列
如果你的邮件发送器配置为支持队列,你可以将消息排队。
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Parameter; $message = (new Message()) ->parameter(new Parameter\Queue( // you may specify the queue to be used: name: 'secondary', // you may specify a delay in seconds: delay: 30, // you may specify how many times to retry: retry: 3, // you may specify a priority: priority: 100, // you may specify if you want to encrypt the message: encrypt: true, // you may specify if you want to render the message templates // before queuing: renderTemplates: false, // true default ));
查看 Symfony 邮件发送器 - 队列支持 了解支持。
使用邮件发送
如果你的邮件发送器支持,你可以定义用于发送消息的邮件发送器。
查看 邮件发送器 了解更多详情。
use Tobento\Service\Mail\Message; use Tobento\Service\Mail\Parameter; $message = (new Message()) ->parameter(new Parameter\SendWithMailer(name: 'mailchimp'));
自定义参数
你可以按照以下方式编写自己的参数
use Tobento\Service\Mail\ParameterInterface; class CustomParameter implements ParameterInterface { /** * Create a new CustomParameter. * * @param string $name */ public function __construct( protected string $name ) {} /** * Returns the name. * * @return string */ public function name(): string { return $this->name; } }
查看 Symfony 自定义参数支持 了解如何处理自定义参数。
邮件发送器
空邮件发送器
NullMailer::class
完全不发送任何邮件消息,这在开发(或测试)时可能很有用。
use Tobento\Service\Mail\NullMailer; use Tobento\Service\Mail\MailerInterface; $mailer = new NullMailer(name: 'null'); var_dump($mailer instanceof MailerInterface); // bool(true)
SF 邮件发送器
文档在 Symfony 邮件发送器 部分。
邮件发送器
你可以使用以下邮件发送器作为你的 Send With Mailer 参数的 MailerInterface
实现。
默认邮件发送器
use Tobento\Service\Mail\Mailers; use Tobento\Service\Mail\MailersInterface; use Tobento\Service\Mail\MailerInterface; $mailers = new Mailers( $mailer, // MailerInterface $anotherMailer, // MailerInterface ); var_dump($mailers instanceof MailersInterface); // bool(true) var_dump($mailers instanceof MailerInterface); // bool(true)
懒加载邮件发送器
懒加载邮件发送器类只在需要时创建邮件发送器。
use Tobento\Service\Mail\LazyMailers; use Tobento\Service\Mail\MailersInterface; use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\MailerFactoryInterface; use Tobento\Service\Mail\Symfony; use Psr\Container\ContainerInterface; $mailers = new LazyMailers( container: $container, // ContainerInterface mailers: [ // using a factory: 'default' => [ // factory must implement MailerFactoryInterface 'factory' => Symfony\SmtpMailerFactory::class, 'config' => [ 'encryption' => '', 'host' => 'host', 'user' => 'user', 'password' => '********', 'port' => 465, // you may define default addresses and parameters // or set to null if defaults are used from email factory. 'defaults' => [ 'from' => 'from@example.com', ], ], ], // using a closure: 'secondary' => static function (string $name, ContainerInterface $c): MailerInterface { // create mailer ... return $mailer; }, 'mailchimp' => [ // ... ], ], ); var_dump($mailers instanceof MailersInterface); // bool(true) var_dump($mailers instanceof MailerInterface); // bool(true)
模板化
以下示例针对默认渲染器 Tobento\Service\Mail\ViewRenderer::class
。
编写视图
use Tobento\Service\Mail\Message; $message = (new Message()) //... ->htmlTemplate( name: 'email/welcome', data: ['name' => 'John', 'text' => 'Lorem ipsum'], );
欢迎视图模板
每个视图中都可用一个名为 message 的变量,它是一个 Tobento\Service\Mail\TemplateMessageInterface::class
的实例。
此外,使用 CSS 文件资源设计你的模板。当模板被渲染时,它将它们转换为内联样式,以更好地支持电子邮件客户端。
<!DOCTYPE html> <html> <head> <title><?= $view->esc($message->subject()) ?></title> <?php // render assets only if inline styles are not used: if (!$withInlineCssStyles) { echo $view->assets()->render(); } ?> <?php // assets can be included in every subview too. $view->asset('email.css'); ?> </head> <body> <?= $view->render('email/header') ?> <h1>Hellow <?= $view->esc($name) ?></h1> <p><?= $view->esc($text) ?></p> <img src="<?= $message->embed('path/to/image.jpg') ?>"> <?php // embed from stream: Psr\Http\Message\StreamInterface // <img src="<?= $message->embed(file: $stream, mimeType: 'image/jpeg') ?>"> // embed from file: Tobento\Service\Filesystem\File // <img src="<?= $message->embed($file) ?>"> ?> <?= $view->render('email/footer') ?> </body> </html>
渲染模板
你可能想要为电子邮件网页视图、调试或其他目的渲染模板。
use Tobento\Service\Mail\RendererInterface; use Tobento\Service\Mail\TemplateInterface; use Tobento\Service\Mail\Template; use Tobento\Service\Mail\Message; class SomeController { public function renderEmail(RendererInterface $renderer): string { // by using a template object: $content = $renderer->renderTemplate( template: new Template( name: 'email/welcome', data: ['name' => 'John'], ), // you may not want to convert css // to inline styles for web views // as you might use CSP with blocking inline css. withInlineCssStyles: false, // true is default ); // render message contents: $message = (new Message()) ->htmlTemplate('email/welcome', ['name' => 'John']); if ($message->getHtml() instanceof TemplateInterface) { $content = $renderer->renderTemplate($message->getHtml()); } return $content; } }
事件
如果你的邮件发送器配置为支持,你可以监听以下事件。
查看 Symfony 邮件发送器 - 事件支持 了解如何创建支持事件的邮件发送器。
Symfony
Symfony 邮件发送器
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\ViewRenderer; use Tobento\Service\View; use Tobento\Service\Dir; use Symfony\Component\Mailer\Transport\Dsn; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory; // create the renderer: $renderer = new ViewRenderer( new View\View( new View\PhpRenderer( new Dir\Dirs( new Dir\Dir('dir/views/'), ) ), new View\Data(), new View\Assets('dir/src/', 'https://example.com/src/') ) ); // create email factory: $emailFactory = new Symfony\EmailFactory( renderer: $renderer, ); // create the transport: $transport = (new EsmtpTransportFactory())->create(new Dsn( 'smtp', 'host', 'user', 'password', 465, [], )); // create the mailer: $mailer = new Symfony\Mailer( name: 'default', emailFactory: $emailFactory, transport: $transport, ); var_dump($mailer instanceof MailerInterface); // bool(true)
查看服务查看以了解更多信息。
事件支持
为了支持事件,您需要将派发器传递给邮件发送器。
use Tobento\Service\Mail\Symfony; use Psr\EventDispatcher\EventDispatcherInterface; // create the mailer: $mailer = new Symfony\Mailer( name: 'default', emailFactory: $emailFactory, transport: $transport, // pass your event dispatcher: eventDispatcher: $dispatcher, // EventDispatcherInterface );
队列支持
为了支持消息队列,您需要将队列处理程序传递给邮件发送器。
考虑使用默认的队列处理程序,使用队列服务
首先,安装队列服务
composer require tobento/service-queue
接下来,将队列处理程序传递给邮件发送器
use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\QueueHandlerInterface; use Tobento\Service\Mail\RendererInterface; use Tobento\Service\Mail\Queue\QueueHandler; use Tobento\Service\Queue\QueueInterface; // create the mailer: $mailer = new Symfony\Mailer( name: 'default', emailFactory: $emailFactory, transport: $transport, // pass your queue handler implementing QueueHandlerInterface: queueHandler: new QueueHandler( queue: $queue, // QueueInterface renderer: $renderer, // RendererInterface // you may define the default queue used if no specific is defined on the message. queueName: 'mails', // null|string ), );
最后,确保作业处理器的容器有以下接口可用
示例使用服务容器作为容器
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\MessageFactoryInterface; use Tobento\Service\Mail\MessageFactory; use Tobento\Service\Queue\JobProcessor; use Tobento\Service\Container\Container; $container = new Container(); $container->set(MessageFactoryInterface::class, MessageFactory::class); $container->set(MailerInterface::class, function() { // create mailer: return $mailer; }); $jobProcessor = new JobProcessor($container);
默认地址和参数
您可以设置默认地址和/或参数,以应用于每条消息
use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\Address; use Tobento\Service\Mail\Parameters; use Tobento\Service\Mail\Parameter; $emailFactory = new Symfony\EmailFactory( renderer: $renderer, // you may pass default addresses or parameters // to be applied to every message created. config: [ 'from' => 'from@example.com', // with object: 'from' => new Address('from@example.com', 'Name'), 'replyTo' => 'reply@example.com', // with object: 'replyTo' => new Address('reply@example.com'), // You may define an address to send all emails to: 'alwaysTo' => 'debug@example.com', // with object: 'alwaysTo' => new Address('debug@example.com'), 'parameters' => new Parameters( new Parameter\PathHeader('Return-Path', 'return@example.com'), ), ], ); // create the mailer: $mailer = new Symfony\Mailer( name: 'default', emailFactory: $emailFactory, transport: $transport, );
HTML 到文本转换
如果您创建的消息没有文本内容,它将根据您的HTML内容创建。
use Tobento\Service\Mail\Message; $message = (new Message()) // will be created from the html: //->text('Lorem Ipsum') ->html('<p>Lorem Ipsum</p>');
Symfony Dsn 邮件发送器工厂
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\ViewRenderer; use Tobento\Service\Mail\Address; use Tobento\Service\Mail\Parameters; use Tobento\Service\Mail\Parameter; use Tobento\Service\View; use Tobento\Service\Dir; // create the renderer: $renderer = new ViewRenderer( new View\View( new View\PhpRenderer( new Dir\Dirs( new Dir\Dir('dir/views/'), ) ), new View\Data(), new View\Assets('dir/src/', 'https://example.com/src/') ) ); // create email factory: $emailFactory = new Symfony\EmailFactory( renderer: $renderer, ); // create the factory: $factory = new Symfony\DsnMailerFactory($emailFactory); // create the mailer: $mailer = $factory->createMailer(name: 'default', config: [ 'dsn' => 'smtp://user:pass@smtp.example.com:port', // If the username, password or host contain // any character considered special in a URI // (such as +, @, $, #, /, :, *, !), // use the following instead of dsn above: //'scheme' => 'smtp', //'host' => 'host', //'user' => 'user', //'password' => '********', //'port' => 465, // you may define default addresses and parameters // or set to null if defaults are used from email factory. 'defaults' => [ 'from' => 'from@example.com', ], ]); var_dump($mailer instanceof MailerInterface); // bool(true)
Symfony Smtp 邮件发送器工厂
use Tobento\Service\Mail\MailerInterface; use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\ViewRenderer; use Tobento\Service\Mail\Address; use Tobento\Service\Mail\Parameters; use Tobento\Service\Mail\Parameter; use Tobento\Service\View; use Tobento\Service\Dir; // create the renderer: $renderer = new ViewRenderer( new View\View( new View\PhpRenderer( new Dir\Dirs( new Dir\Dir('dir/views/'), ) ), new View\Data(), new View\Assets('dir/src/', 'https://example.com/src/') ) ); // create email factory: $emailFactory = new Symfony\EmailFactory( renderer: $renderer, ); // create the factory: $factory = new Symfony\SmtpMailerFactory($emailFactory); // create the mailer: $mailer = $factory->createMailer(name: 'default', config: [ 'encryption' => '', 'host' => 'host', 'user' => 'user', 'password' => '********', 'port' => 465, // you may define default addresses and parameters // or set to null if defaults are used from email factory. 'defaults' => [ 'from' => 'from@example.com', ], ]); var_dump($mailer instanceof MailerInterface); // bool(true)
Symfony 自定义参数支持
为了支持自定义参数,您可以编写一个新的电子邮件工厂类或扩展默认类
use Tobento\Service\Mail\Symfony; use Tobento\Service\Mail\MessageInterface; use Tobento\Service\Mail\ParameterInterface; use Symfony\Component\Mime\Email; class CustomizedEmailFactory extends Symfony\EmailFactory { /** * Create email from message. * * @param MessageInterface $message * @return Email */ public function createEmailFromMessage(MessageInterface $message): Email { $email = parent::createEmailFromMessage($message); // filter your custom parameters: $parameters = $message->parameters()->filter( fn(ParameterInterface $p): bool => $p instanceof CustomParameter ); // do something with it: foreach($parameters as $parameter) {} } }