doujia4041 2019-05-19 16:49
浏览 105

如果附件很大,phpmailer会突然停止

Problem

I have a script to send newsletters to my contacts. The script works fine, if I have no attachement (proven with 2,000 emails).

Now, if I use attachments, the script works fine too. But only by sending about 30 emails.

Test-Setup

  • group of 100 emails to be performed (loop).
  • attachments: 2 files (total 4,6 MB) --> the script terminates (but without error message) after 50 seconds and 34 emails sent. (~ 156 MB).

Test-Variations

  • changing in php.ini memory_limit from 100M to 500M --> has no effect. Still error after 34 emails.
  • putting a sleep(5) after each loop --> has no effect. Still error after 34 emails.
  • no attachments: all 100 emails are sent (about 30 seconds).
  • no attachements on 2000 emails: all 2000 emails are sent (about 6 minutes).
  • no effect of changing max_execution_time in php.ini.

Assumtions

Due to the behaviour I expect to have a memory-problem and not a time-problem.

But a test of the memory (memory_get_usage()) in each loop showed that the memory of the first loop is 1.1 MBand of the 34th loop 1.2 MB.

Question

Please find below my code, but I guess it sould be ok. Does anybody has an idea what is causing the issue? Many thanks!

myMailer.class

class myMailer extends PHPMailer {

    public function __construct(?bool $exceptions = true) {
        $config = parse_ini_file('../../ini/config.ini', true);

        parent::__construct($exceptions);

        try {
            // Language of Errors
            $this->setLanguage('en', dirname(__FILE__) . '/../external/PHPMailer/language/');

            // Server settings
            $this->SMTPDebug  = 0;                                  // Enable verbose debug output
            $this->isSMTP();                                        // Set mailer to use SMTP
            $this->SMTPAuth   = true;                               // Enable SMTP authentication
            $this->SMTPSecure = 'ssl';                              // Enable TLS encryption, `ssl` also accepted
            $this->Port       = 465;                                // TCP port to connect to
            $this->Host       = $config['smtp_server']['host'];     // SMTP server
            $this->Username   = $config['smtp_server']['username']; // SMTP username
            $this->Password   = $config['smtp_server']['password']; // SMTP password
            $this->CharSet    ='UTF-8';                             // Set Character Set
            $this->isHTML(true);                                    // Set email format to HTML
            $this->setFrom("office@superman.com", "superman OFFICE");

        } catch (Exception $e) {
            // echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
            trigger_error("myMailer(): " . $this->ErrorInfo,E_USER_ERROR);
        }
    }

    public function setBody(string $salutation, string $receiver, string $message) : void {
        // Linebreak to <BR>
        $message = nl2br($message);

        // Create Body
        $body = file_get_contents('../../ini/email_template.html');
        $body = str_replace("{Receiver}",       $receiver,                  $body);
        $body = str_replace("{Salutation}",     $salutation,                $body);
        $body = str_replace("{Message}",        $message,                   $body);

        $this->Body = $body;
    }
}

send_emails.php

<?php
$groupID     = $_POST['id'];
$subject     = $_POST['Subject'];
$message     = $_POST['Message'];
$attachments = (key_exists('Files', $_POST)) ? $_POST['Files'] : array();

$group = new Group($groupID);

// Prepare and get HTML
$htmlGenerator  = HtmlGenerator::getInstance();
$htmlGenerator->setTitle("sending emails");
$header = $htmlGenerator->getWebbaseHtmlHeader();
$footer = $htmlGenerator->getWebbaseHtmlFooter();

echo $header;

?>
    <div class="container">
        <h1>Emailing</h1>
        <h3>Group: <?php echo $group->getTitle(); ?></h3>
        <h4>Number of members: <?php echo $group->getNumberOfMembers(); ?></h4>
        <?php

            $strScreenOutput = "
                <table class='table table-sm table-hover table-responsive-md'>
                    <thead class='thead-dark'>
                        <th scope='col'>Nr.</th>
                        <th scope='col'>Date</th>
                        <th scope='col'>Company</th>
                        <th scope='col'>Lastname</th>
                        <th scope='col'>Firstname</th>
                        <th scope='col'>Email</th>
                        <th scope='col'>Newsletter</th>
                        <th scope='col'>Result</th>
                    </thead>
                    <tbody>";

            $protocol = $strScreenOutput;

            echo $strScreenOutput;

            $i = 0;
            $members  = Contact::getAllOfGroup($groupID);
            foreach ($members as $contact) {
                $i++;
                $datRun = date('d.m.Y (H:i:s)');

                if ($contact->getNewsletter() === false) {
                    $result = "no sub.";
                } elseif ($contact->getEmail() === "" || $contact->getEmail() === null) {
                    $result = "no email";
                } else {
                    $return = $contact->sendEmail($subject, $message, $attachments);
                    $result = ($return===true) ? "ok" : $return;
                }

                // create feedback for browser
                $strScreenOutput  = "<tr>";
                $strScreenOutput .= "<th scope='row'>".str_pad($i, 4 ,'0', STR_PAD_LEFT)."</th>";
                $strScreenOutput .= "<td>{$datRun}</td>";
                $strScreenOutput .= "<td>{$contact->getCompany()}</td>";
                $strScreenOutput .= "<td>{$contact->getLastName()}</td>";
                $strScreenOutput .= "<td>{$contact->getFirstName()}</td>";
                $strScreenOutput .= "<td>{$contact->getEmail()}</td>";
                $strScreenOutput .= "<td>".(($contact->getNewsletter()) ? "yes" : "no")."</td>";
                $strScreenOutput .= "<td>{$result}</td>";
                $strScreenOutput .= "</tr>";
                echo str_pad($strScreenOutput,4096)."
"; // Add some additional blanks to enable flushing (as some browsers suppress flushing)

                // create internal protocol
                $protocol .= $strScreenOutput . "
";

                // Send to browser
                flush();
                ob_flush();

                // add some execution time
                set_time_limit(30);
            }

            $strScreenOutput = "</tbody>
                            </table>";
            $protocol .= $strScreenOutput;
            echo $strScreenOutput;
        ?>

        <h3>Emails successfully transmitted.</h3>
    </div>

<?php

    // send protocols
    $protocol = "<h3>Group: {$group->getTitle()}</h3>".$protocol;
    $protocol = "<div style='margin-top: 30px'>$protocol</div>";

    $internal = new Contact(1);
    $internal->sendEmail("PROTOCOL: ".$subject, $message . $protocol, $attachments);

echo $footer;

Contact::sendEmail()

public  function sendEmail(string $subject, string $message, array $attachments = array()) {
    $mail = new myMailer();

    if ( is_null($this->getEmail() || $this->getEmail() == "") ) {
        return false;

    } else {
        try {
            // Compose Email
            $mail->addAddress($this->getEmail(), $this->getFirstName() . " " . $this->getLastName());
            $mail->Subject = $subject;
            $mail->setBody($this->getSalutationText(), $this->getAddress(), $message);

            foreach ($attachments as $file) {
                $uploadPath  = $_SERVER['DOCUMENT_ROOT'] . "/../../files/email_attachments/";
                $file_url = $uploadPath.$file;

                if (! is_dir($uploadPath))    { die("Folder \"$uploadPath\" does not exist.");}
                if (! file_exists($file_url)) { die("File \"$file_url\" does not exist."); }

                $mail->addAttachment($file_url);
            }

            $return = $mail->send();

            // clean up
            $mail->clearAddresses();
            $mail->clearAttachments();

        } catch (Exception $error) {
            $return = $error->getMessage();
        }

        return $return;
    }

    unset($mail);

}
  • 写回答

1条回答 默认 最新

  • drd43058 2019-05-19 20:21
    关注

    It does sound like a memory problem - PHPMailer isn't very efficient on memory when sending large attachments - try echoing what you get from memory_get_usage() inside your loop to confirm memory consumption.

    Generally though, you're sending quite inefficiently because you're creating a new PHPMailer instance, reprocessing the same attachments, and opening a new SMTP connection for every message, all of which only need doing once. For how to send more efficiently, look at the mailing list example provided with PHPMailer, and the wiki doc on sending to lists.

    A more efficient reuse of instance and connection may reduce your overall memory requirements.

    评论

报告相同问题?

悬赏问题

  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来