You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过PHP从php://stdin重发邮件并替换主题为别名

Postfix + PHP 邮件转发脚本:提取别名替换主题的实现细节

先理清楚整个流程:Postfix 把目标邮件投递到你写的 PHP 脚本 → 脚本从 php://stdin 读取原始邮件内容 → 用 MailMimeParser 解析邮件头和正文 → 从 To 字段里提取别名 → 把邮件主题替换为该别名 → 用 PHPMailer 重新发送出去。下面拆解每个环节的关键细节:

一、读取并解析原始邮件

这一步是基础,你之前能把 stdin 内容写入文件,说明读取没问题,但解析环节容易踩坑。用 MailMimeParser 要注意必须一次性读全 stdin 的内容,不能分段读取:

use ZBateson\MailMimeParser\Parser;

// 一次性读取stdin中的完整原始邮件
$rawEmail = file_get_contents('php://stdin');
$parser = new Parser();
$message = $parser->parse($rawEmail);

$message 对象会封装邮件的所有头信息、正文、附件,后续操作全依赖这个对象。

二、从 To 字段提取别名

To 字段的格式可能是「显示名」<alias@domain.com>或者直接alias@domain.com,不用自己写正则匹配,MailMimeParser 已经做好了解析:

// 获取所有收件人地址对象
$toAddresses = $message->getHeader('To')->getAddresses();
// 提取第一个收件人邮箱@前面的部分,也就是你要的别名
$alias = $toAddresses[0]->getLocalPart();

不管 To 字段有没有显示名,getLocalPart()都能精准提取邮箱的本地部分。如果有多个收件人,根据你的业务需求调整提取逻辑即可。

三、替换主题并重发邮件

用 PHPMailer 重发时,别用 SMTP 模式(容易因配置错误导致脚本无响应),直接用 Postfix 本地的 sendmail 模式更适配环境。另外要保留原始邮件的正文、附件,只替换主题:

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);
try {
    // 用本地sendmail投递,避免SMTP配置问题
    $mail->isSendmail();
    // 保留原始发件人信息
    $fromAddr = $message->getHeader('From')->getAddresses()[0];
    $mail->setFrom($fromAddr->getEmail(), $fromAddr->getName());
    
    // 设置重发的目标邮箱
    $mail->addAddress('your-target@domain.com');
    
    // 将主题替换为提取到的别名
    $mail->Subject = $alias;
    
    // 处理正文:区分HTML和纯文本格式
    if ($message->getHtmlContent()) {
        $mail->isHTML(true);
        $mail->Body = $message->getHtmlContent();
        $mail->AltBody = $message->getTextContent() ?: strip_tags($message->getHtmlContent());
    } else {
        $mail->isHTML(false);
        $mail->Body = $message->getTextContent();
    }
    
    // 处理所有附件
    foreach ($message->getAllAttachmentParts() as $attachment) {
        $mail->addStringAttachment(
            $attachment->getContent(),
            $attachment->getFilename(),
            PHPMailer::ENCODING_BASE64,
            $attachment->getContentType()
        );
    }
    
    $mail->send();
} catch (Exception $e) {
    // 将错误写入日志方便排查
    error_log("邮件重发失败: " . $mail->ErrorInfo);
}

之前脚本无反应大概率是 SMTP 配置错误,换成isSendmail()直接调用 Postfix 本地投递,基本不会有问题。

四、Postfix 配置核对要点

你已经配置了 master.cf、transport 等文件,再核对几个关键项:

  1. master.cf 中的脚本投递条目:
my-mail-script unix  -       n       n       -       -       pipe
  flags=F user=www-data argv=/usr/bin/php /绝对路径/你的脚本.php
  • user要设置为有脚本执行权限的用户(比如 www-data 或 PHP 运行用户)
  • argv中的 PHP 路径要准确,可用which php查看真实路径
  1. transport 映射配置:
your-alias-domain.com   my-mail-script:

配置完成后必须执行postmap /etc/postfix/transport生成哈希表,否则 Postfix 不会识别
3. main.cf 启用 transport 映射:

transport_maps = hash:/etc/postfix/transport

五、常见问题排查

  1. 脚本无反应,Postfix 日志报错:
    • 查看/var/log/mail.log,如果出现pipe command exited with status 127,要么是 PHP 路径错误,要么是脚本不存在/无执行权限
    • 脚本开头必须添加#!/usr/bin/env php,告诉系统用 PHP CLI 执行
  2. 重发邮件无主题:
    • 添加error_log("提取到的别名: " . $alias)日志,确认别名是否为空
    • 检查 PHPMailer 的Subject属性是否被后续代码覆盖

完整可运行脚本

整合上述代码,加上完整错误处理:

#!/usr/bin/env php
<?php

use ZBateson\MailMimeParser\Parser;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require __DIR__ . '/vendor/autoload.php';

error_log("邮件处理脚本启动");

try {
    $rawEmail = file_get_contents('php://stdin');
    if (empty($rawEmail)) {
        throw new Exception("未从stdin读取到邮件内容");
    }

    $parser = new Parser();
    $message = $parser->parse($rawEmail);

    // 提取别名
    $toHeader = $message->getHeader('To');
    if (!$toHeader) {
        throw new Exception("邮件无To字段");
    }
    $toAddresses = $toHeader->getAddresses();
    if (empty($toAddresses)) {
        throw new Exception("To字段无有效地址");
    }
    $alias = $toAddresses[0]->getLocalPart();
    if (empty($alias)) {
        throw new Exception("无法提取别名");
    }
    error_log("提取到别名: {$alias}");

    // 重发邮件
    $mail = new PHPMailer(true);
    $mail->isSendmail();

    // 设置发件人
    $fromAddresses = $message->getHeader('From')->getAddresses();
    if (!empty($fromAddresses)) {
        $fromAddr = $fromAddresses[0];
        $mail->setFrom($fromAddr->getEmail(), $fromAddr->getName());
    } else {
        $mail->setFrom('noreply@yourdomain.com', '系统自动转发');
    }

    // 设置目标收件人
    $mail->addAddress('your-target@domain.com');

    // 替换主题
    $mail->Subject = $alias;

    // 处理正文
    $htmlContent = $message->getHtmlContent();
    $textContent = $message->getTextContent();
    if ($htmlContent) {
        $mail->isHTML(true);
        $mail->Body = $htmlContent;
        $mail->AltBody = $textContent ?: strip_tags($htmlContent);
    } else {
        $mail->isHTML(false);
        $mail->Body = $textContent;
    }

    // 处理附件
    foreach ($message->getAllAttachmentParts() as $attachment) {
        $mail->addStringAttachment(
            $attachment->getContent(),
            $attachment->getFilename(),
            PHPMailer::ENCODING_BASE64,
            $attachment->getContentType()
        );
    }

    $mail->send();
    error_log("邮件重发成功,主题: {$alias}");
} catch (Exception $e) {
    error_log("邮件处理失败: " . $e->getMessage());
    exit(1); // 返回非0状态码,让Postfix重试
}

exit(0);

内容的提问来源于stack exchange,提问作者konkaGhe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:07:15