定制 tobento/service-mail 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

tobento/service-mail

Composer 安装命令:

composer require tobento/service-mail

包简介

Mailer interface for PHP applications.

README 文档

README

Mailer interface for PHP applications using the Symfony Mailer as default mailer implementation.

Table of Contents

Getting started

Add the latest version of the mail service project running this command.

composer require tobento/service-mail

Requirements

  • PHP 8.4 or greater

Highlights

  • Framework-agnostic, will work with any project
  • Decoupled design

Documentation

Basic Usage

Creating And Sending Messages

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);
    }
}

Check out the available Mailers.

Check out the Message to learn more about it.

Message

Email Addresses

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'));

Contents

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', []);

Headers

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'),
    ));

File Attachments

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',        
    ));    

Tags And Metadata

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',
    ]));

Queue

You may queue your message if your mailer is configured to support it.

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
    ));

Check out the Symfony Mailer - Queue Support for support.

Send With Mailer

You may define a mailer used to send the message if your mailer supports it.

Check out the Mailers for more detail.

use Tobento\Service\Mail\Message;
use Tobento\Service\Mail\Parameter;

$message = new Message()
    ->parameter(new Parameter\SendWithMailer(name: 'mailchimp'));

Custom Parameters

You may write your own parameters in the following way:

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;
    }
}

Check out the Symfony Custom Parameters Support for handling your custom parameter.

Mailer

Null Mailer

The NullMailer::class does not send any mail message at all which may be useful while developing (or testing).

use Tobento\Service\Mail\NullMailer;
use Tobento\Service\Mail\MailerInterface;

$mailer = new NullMailer(name: 'null');

var_dump($mailer instanceof MailerInterface);
// bool(true)

SF Mailer

Documentation is in the Symfony Mailer section.

Mailers

You may use the following mailers as your MailerInterface implementation for supporting the Send With Mailer parameter.

Default Mailers

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)

Lazy Mailers

The lazy mailers class creates the mailer only on demand.

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)

Templating

The following examples are aimed for the default renderer Tobento\Service\Mail\ViewRenderer::class.

Writing Views

use Tobento\Service\Mail\Message;

$message = new Message()
    //...
    ->htmlTemplate(
        name: 'email/welcome',
        data: ['name' => 'John', 'text' => 'Lorem ipsum'],
    );

The welcome view template

A variable called message, which is an instance of Tobento\Service\Mail\TemplateMessageInterface::class is available on every view.

Furthermore, use css file assests to design your template. When the template gets rendered, it will convert them to inline styles for better email clients support.

<!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>

Render Templates

You may want to render a template for an email web view, debugging or other purposes.

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;
    }
}

Events

You may listen to the following events if your mailer is configured to support it.

Event Description
Tobento\Service\Mail\Event\MessageSent::class The Event will be fired after sending the message.
Tobento\Service\Mail\Event\MessageNotSent::class The Event is fired if the message could not be sent.
Tobento\Service\Mail\Event\MessageQueued::class The Event will be fired after queuing the message.

Check out the Symfony Mailer - Events Support to create the mailer supporting events.

Symfony

Symfony Mailer

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)

Check out the View Service to learn more about it.

Events Support

In order to support events you will need to pass a dispatcher to the mailer:

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
);

Queue Support

In order to support queuing messages you will need to pass a queue handler to the mailer.

Consider using the default queue handler using the Queue Service:

First, install the queue service:

composer require tobento/service-queue

Next, pass the queue handler to the mailer:

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
    ),
);

Finally, make sure the container of the job processor has the following interfaces available:

Example using the Service Container as container:

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);

Default Addresses And Parameters

You may set default addresses and/or parameters to be applied to every message:

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 To Text Converting

If you create a message without text content, it will be created from your html content.

use Tobento\Service\Mail\Message;

$message = new Message()
    // will be created from the html:
    //->text('Lorem Ipsum')
    
    ->html('<p>Lorem Ipsum</p>');

Symfony Dsn Mailer Factory

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 Mailer Factory

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 Custom Parameters Support

In order to support your custom parameters you could write a new email factory class or extend the default:

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) {}
    }
}

Credits

tobento/service-mail 适用场景与选型建议

tobento/service-mail 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 97 次下载、GitHub Stars 达 0, 最近一次更新时间为 2023 年 11 月 05 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「mail」 「package」 「php」 「email」 「tobento」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 tobento/service-mail 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 tobento/service-mail 我们能提供哪些服务?
定制开发 / 二次开发

基于 tobento/service-mail 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 97
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 24
  • 依赖项目数: 3
  • 推荐数: 1

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2023-11-05