定制 jardisadapter/mailer 二次开发

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

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

jardisadapter/mailer

Composer 安装命令:

composer require jardisadapter/mailer

包简介

SMTP mail client with STARTTLS, authentication, HTML and plain text support, attachments, and retry with exponential backoff; a building block of the open-source foundation that Jardis-generated DDD code runs on

README 文档

README

Build Status License: MIT PHP Version PHPStan Level PSR-12 Zero Dependencies

Part of Jardis — the Domain-Driven Design platform for PHP. You model your domain; Jardis generates the production-ready hexagonal code (DTOs, Command/Query handlers, repositories, persistence). This package is part of the open-source foundation that generated code runs on.

Transactional emails without the bloat. A lean SMTP mailer for PHP built on raw sockets — covers order confirmations, password resets, and notifications. No Swiftmailer, no Symfony Mailer, no dependency tree. Just SMTP over a socket.

* Zero external PHP packages. Only ext-openssl + ext-mbstring + jardissupport/contract (interfaces only).

Why This Mailer?

  • Two classes are enoughMailer + SmtpConfig, nothing else to learn
  • Fluent message builder — immutable, with* pattern like PSR-7
  • STARTTLS + implicit SSL — secure by default, PORT 587 or 465
  • AUTH LOGIN & PLAIN — standard SMTP authentication
  • HTML + plain text — multipart/alternative, just works
  • File attachments & inline images — Base64 encoded, Content-ID for HTML embedding
  • Retry with backoff — automatic retry on connection errors and temporary SMTP failures
  • Connection keepalive — send 100 emails over one SMTP connection
  • NOOP health-check — stale connections are silently reconnected
  • 96% test coverage — integration tests against a real SMTP server, not mocks

Installation

composer require jardisadapter/mailer

Quick Start

Send a Simple Email

use JardisAdapter\Mailer\Mailer;
use JardisAdapter\Mailer\Config\SmtpConfig;
use JardisAdapter\Mailer\Data\MailMessage;

$mailer = new Mailer(new SmtpConfig(
    host: 'smtp.example.com',
    username: 'user@example.com',
    password: 'secret',
));

$message = MailMessage::create()
    ->withFrom('noreply@example.com', 'My App')
    ->withTo('customer@example.com', 'Jane Doe')
    ->withSubject('Your Order Confirmation')
    ->withText('Thank you for your order #1234.')
    ->withHtml('<h1>Thank you!</h1><p>Your order #1234 has been confirmed.</p>');

$mailer->send($message);

HTML + Plain Text

$message = MailMessage::create()
    ->withFrom('noreply@example.com')
    ->withTo('user@example.com')
    ->withSubject('Weekly Report')
    ->withText('Your weekly report is attached.')
    ->withHtml('<h1>Weekly Report</h1><p>See attachment.</p>');

Both bodies are sent as multipart/alternative — the recipient's mail client picks the best one.

Attachments

$message = MailMessage::create()
    ->withFrom('billing@example.com')
    ->withTo('customer@example.com')
    ->withSubject('Your Invoice')
    ->withText('Please find your invoice attached.')
    ->withAttachment(file_get_contents('invoice.pdf'), 'invoice.pdf', 'application/pdf')
    ->withAttachment($csvData, 'report.csv', 'text/csv');

Inline Images in HTML

$message = MailMessage::create()
    ->withFrom('news@example.com')
    ->withTo('subscriber@example.com')
    ->withSubject('Our Newsletter')
    ->withHtml('<h1>News</h1><img src="cid:logo">')
    ->withEmbeddedImage(file_get_contents('logo.png'), 'logo.png', 'image/png');

Multiple Recipients, CC, BCC

$message = MailMessage::create()
    ->withFrom('team@example.com')
    ->withTo('alice@example.com', 'Alice')
    ->withTo('bob@example.com', 'Bob')
    ->withCc('manager@example.com')
    ->withBcc('archive@example.com')
    ->withReplyTo('support@example.com')
    ->withSubject('Meeting Notes')
    ->withText('Notes from today.');

Custom Headers

$message = MailMessage::create()
    ->withFrom('alerts@example.com')
    ->withTo('admin@example.com')
    ->withSubject('Server Alert')
    ->withText('CPU at 95%')
    ->withHeader('X-Priority', '1')
    ->withHeader('X-Mailer', 'Jardis Mailer');

Fully Configured

$mailer = new Mailer(new SmtpConfig(
    host: 'smtp.example.com',
    port: 587,                    // Default: 587
    encryption: 'tls',            // 'tls' (STARTTLS), 'ssl' (implicit), 'none'
    username: 'user@example.com',
    password: 'secret',
    timeout: 30,                  // Connect + read/write timeout in seconds
    fromAddress: 'noreply@example.com',  // Default From (applied when not set on message)
    fromName: 'My Application',
    maxRetries: 3,                // Retry on connection errors and 4xx
    retryDelayMs: 200,            // Exponential backoff: 200ms, 400ms, 800ms
));

Batch Sending

Send multiple emails over a single SMTP connection — the connection stays alive between messages:

$messages = [];
foreach ($recipients as $recipient) {
    $messages[] = MailMessage::create()
        ->withFrom('noreply@example.com')
        ->withTo($recipient->email, $recipient->name)
        ->withSubject('Your monthly statement')
        ->withHtml($renderer->render($recipient));
}

$result = $mailer->sendBatch($messages);

echo $result->successCount() . ' sent, ' . $result->failureCount() . ' failed';

foreach ($result->failed() as $failure) {
    log($failure['message']->to(), $failure['error']->getMessage());
}

Retry

$mailer = new Mailer(new SmtpConfig(
    host: 'smtp.example.com',
    maxRetries: 3,          // Up to 3 retries
    retryDelayMs: 200,      // Exponential backoff: 200ms, 400ms, 800ms
));

Automatically retries on SmtpConnectionException and temporary SMTP errors (4xx). No retry on permanent errors (5xx) — those are recipient or policy errors.

Error Handling

Exception When
SmtpConnectionException Host unreachable, TLS handshake failure, timeout
SmtpAuthenticationException LOGIN or PLAIN rejected
SmtpTransportException SMTP protocol error (rejected recipient, DATA error)
MailMessageException Invalid message (missing From, To, or body)

All implement MailerExceptionInterface for generic catch:

use JardisSupport\Contract\Mailer\MailerExceptionInterface;

try {
    $mailer->send($message);
} catch (MailerExceptionInterface $e) {
    // Any mailer error
}

Encryption

Port Encryption How it works
587 tls (default) Connects plain, upgrades via STARTTLS
465 ssl Connects over implicit TLS
25 none No encryption (not recommended)

Architecture

The user only sees Mailer + SmtpConfig + MailMessage. Internally, the mailer orchestrates a pipeline of invokable handlers — built from the config:

Mailer (Orchestrator)
  │
  │  Transformers (MailMessage → MailMessage, built from config):
  │  ├── DefaultFrom          apply default sender if not set
  │  └── MessageValidator     validate before sending
  │
  │  Encoder (MailMessage → Envelope):
  │  └── MimeEncoder          MIME assembly, Base64, Quoted-Printable, RFC 2047
  │
  │  Transport (Envelope → void):
  │  └── SmtpTransport        socket-based SMTP with NOOP health-check
  │
  │  Retry (internal to Mailer):
  │  └── Exponential backoff on connection errors and 4xx
  │
  ▼
  send():
    foreach transformer → $message = $transform($message)
    $envelope = $encoder($message)
    $transport($envelope)        // with retry

Each handler is an invokable object (__invoke) — independently testable, replaceable, composable. Only what is configured gets instantiated.

Custom Transport

The transport is a closure — replaceable for testing or alternative delivery:

$mailer = new Mailer(
    config: new SmtpConfig(host: 'localhost'),
    transport: function (Envelope $envelope): void {
        // Log, mock, or send via API
        file_put_contents('/tmp/mail.log', $envelope->rawMessage);
    },
);

Jardis Foundation Integration

In a Jardis DDD project, the mailer is automatically configured via ENV:

MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_USERNAME=user@example.com
MAIL_PASSWORD=secret
MAIL_TIMEOUT=30
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME=My Application

The MailerHandler in JardisApp builds the mailer and registers it in the ServiceRegistry. Your application code receives MailerInterface via injection — without ever importing Mailer directly.

Development

cp .env.example .env    # One-time setup
make install             # Install dependencies
make phpunit             # Run tests
make phpstan             # Static analysis (Level 8)
make phpcs               # Coding standards (PSR-12)

Documentation

Full documentation, guides, and API reference:

docs.jardis.io/en/adapter/mailer

License

MIT License — free for any use, including commercial.

AI-Assisted Development

This package ships with a skill for Claude Code, Cursor, Continue, and Aider. Install it in your consuming project:

composer require --dev jardis/dev-skills

More details: https://docs.jardis.io/en/skills

jardisadapter/mailer 适用场景与选型建议

jardisadapter/mailer 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 110 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 02 日, 在 PHP 生态内属于活跃度较高的组件。

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

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

围绕 jardisadapter/mailer 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-02