phpmailer внезапно останавливается в случае больших вложений - PullRequest
1 голос
/ 19 мая 2019

Проблема

У меня есть скрипт для отправки информационных бюллетеней моим контактам.Скрипт работает нормально, если у меня нет вложения (доказано 2000 писем).

Теперь, если я использую вложения, скрипт тоже работает отлично.Но только отправив около 30 электронных писем.

Test-Setup

  • группа из 100 электронных писем, которые необходимо выполнить (цикл).
  • вложения: 2 файла (всего 4,6 МБ) -> сценарий завершается (но без сообщения об ошибке) через 50 секунд и отправлено 34 электронных письма.(~ 156 МБ).

Варианты тестирования

  • изменение в php.ini memory_limit со 100M до 500M -> не имеетэффект.Все еще ошибка после 34 электронных писем.
  • вставка sleep(5) после каждого цикла -> не имеет никакого эффекта.Все еще ошибка после 34 писем.
  • без вложений: все 100 писем отправлены (около 30 секунд).
  • без вложений на 2000 писем: все 2000 писем отправлены (около 6 минут).
  • без эффекта изменения max_execution_time в php.ini.

Допущения

Из-за ожидаемого поведенияпроблема, а не проблема времени.

Но проверка памяти (memory_get_usage()) в каждом цикле показала, что память первого цикла равна 1.1 MB, а 34-го цикла 1.2 MB.

Вопрос

Пожалуйста, найдите мой код ниже, но я думаю, что все будет в порядке.У кого-нибудь есть идея, что является причиной проблемы?Большое спасибо!

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)."\n"; // Add some additional blanks to enable flushing (as some browsers suppress flushing)

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

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

Контактное лицо:: 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 Ответ

0 голосов
/ 19 мая 2019

Это звучит как проблема с памятью - PHPMailer не очень эффективен в памяти при отправке больших вложений - попробуйте повторить то, что вы получаете от memory_get_usage () внутри цикла, чтобы подтвердить потребление памяти.

Как правило, вы отправляете довольно неэффективно, потому что вы создаете новый экземпляр PHPMailer, перерабатываете те же вложения и открываете новое SMTP-соединение для каждого сообщения, и все это нужно сделать только один раз.Чтобы узнать, как сделать отправку более эффективным, посмотрите пример списка рассылки, предоставляемый с PHPMailer , и вики-документ по отправке в списки .

Более эффективное повторное использование экземпляраи подключение может снизить ваши общие требования к памяти.

...