如何处理 transient/intermittent PHPMailer SMTP 连接错误

How to handle transient/intermittent PHPMailer SMTP connect errors

我搜索了 Whosebug 和 PHPMailer 问题,但没有成功。有很多永久性 PHPMailer SMTP 连接错误,但 none 我可以在瞬态或间歇性错误中看到。

我正在使用 PHPMailer (6.2) 和 PHP 8.0 从我的远程 Web 服务器向 smtp.office365.com(Microsoft Exchange Online 服务)发送电子邮件。大多数时候它工作正常。

间歇性地,我怀疑是由于网络级别或 SMTP 服务器级别的拥塞,我看到以下消息:

SMTP 错误:无法连接到服务器:连接被拒绝 (111)

SMTP 错误:无法连接到服务器:连接超时

然后:SMTP connect() 失败。

由于与 SMTP 服务器的初始连接远离托管 PHPMailer 脚本的 Web 服务器,这意味着我不能依赖 SMTP 服务器处理电子邮件排队和重试。

我已经输入了一些重试 $email-> 发送命令的基本代码,但到目前为止,当 SMTP 连接失败时,它从未成功通过。

我正在寻求有关如何处理(在 PHPMailer 中)这些暂时性错误的建议?

非常感谢。

我的服务器 PHP 脚本(提取):

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

require ($_SERVER['DOCUMENT_ROOT'] . '/vendor/phpmailer/phpmailer/src/Exception.php');
require ($_SERVER['DOCUMENT_ROOT'] . '/vendor/phpmailer/phpmailer/src/PHPMailer.php');
require ($_SERVER['DOCUMENT_ROOT'] . '/vendor/phpmailer/phpmailer/src/SMTP.php');      

// Load Composer's autoloader
require $_SERVER['DOCUMENT_ROOT'] . '/vendor/autoload.php';  
$email = new PHPMailer(true);//True means Throw errors

try {
    //Server settings
    $email->SMTPDebug = SMTP::DEBUG_CONNECTION;
    $debug = '';
    $email->Debugoutput = function($str, $level) {
        $GLOBALS['debug'] .= "$level: $str\n";
    };

    $email->isSMTP(); // Set mailer to use SMTP
    $email->CharSet = 'UTF-8';  //not important
    $email->Host = 'smtp.office365.com'; // Specify main and backup SMTP servers
    $email->SMTPAuth = true; // Enable SMTP authentication
    $email->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; 
    $email->Port = 587; // TCP port to connect to
    //Authentication
    $email->Username = ‘Myusername’; // SMTP username
    $email->Password = 'mypassword'; // SMTP password - must be App Password from Microsoftt


     //Recipients
      $email->SetFrom(‘MySenderemail’);
      $email->addReplyto(‘Myreplytoemail’, 'No-Reply');
     $email->addAddress( ‘MyTargetemailaddress’);

    // Content
    $email->isHTML(true); 
    $email->Subject   = $mailsubject;
    $email->Body      = $messagebody;
    $email->AltBody = 'This email can only be viewed in HTML. Please contact the SSN IT Coordinator.';

    //Send the email
    $email->send();
    elog($ModuleName . '|' . $authentic . ': ' . 'Mail Sent Successfully ' . ' ' . $mailsubject);
    echo($ModuleName . '|' . $authentic . ': ' . 'Mail Sent Successfully ' . ' ' . $mailsubject);
    return true;

} catch (Exception $e) {
    error_log($email->ErrorInfo);
    error_log($debug);
    if (strpos($email->ErrorInfo, 'SMTP connect() failed') !== false) {
        // Retry sending the email in case it was a transient error
        error_log ($ModuleName . '|' . $authentic . ': ' . 'First attempt at email failed, retrying: ' .  $email->ErrorInfo . ' ' . $mailsubject);
        try {$email->send();} catch (exception $e){
            //Failed second time so send error
            http_response_code(500);
            if (!headers_sent()) {header('HTTP/1.1 500 Internal Server Error');}
            elog($ModuleName . '|' . $authentic . ': ' . 'Retry Mail not sent ' . $mailsubject . $authentic . $e->getMessage());
           die($ModuleName . '|' . $authentic . ': ' . 'Retry Mail not sent ' . $mailsubject . $authentic . $e->getMessage());
        }
    } else {
        //Some error other than SMTP Connect failed so return it.
        http_response_code(500);
        if (!headers_sent()) {header('HTTP/1.1 500 Internal Server Error');}
        error_log ($ModuleName . '|' . $authentic . ': ' . 'Mail NOT SENT ' . $mailsubject . $authentic . $e->getMessage());
        die($ModuleName . '|' . $authentic . ': ' . 'Mail NOT SENT ' . $mailsubject . $authentic . $e->getMessage());
    }
}

您的连接困难似乎有时间因素,因此立即重试不太可能比第一次成功。我推荐的方法也是 PHPMailer 文档推荐的方法:安装本地邮件服务器并通过它进行中继。它将管理排队、重试、指数退避和退回,您的发送脚本将 运行 快得多。这比尝试在 PHP 中编写邮件服务器要有效和可靠得多,这正是您正在尝试做的。

旁注:由于您使用的是 Composer,因此可以删除 PHPMailer 类 的 require 行; composer 的自动加载器会自动为你加载 类。