How to add array offset for newsletter submission?

3

I have to send a newsletter to all network users. The problem is that the array , containing the result of the query, has about eight thousand records. How can I optimize it so it does not overflow the memory limit? Adding ini_set('memory_limit', '-1'); was not enough.

function enviarNewsletterUsuarios(){
    $usuarios = getUsuarios();


if(!empty($usuarios) OR !isset($usuarios)){


    foreach ($usuarios as $usuario) {


        $objetivo   = getObjetivo($usuario->guid);
        $ideias     = getIdeiaPorObjetivo($objetivo, $usuario->cidade);


        $mail = new PHPMailer;


        $mail->isSMTP();                                      // Set mailer to use SMTP
        $mail->Host = 'host';                 // Specify main and backup SMTP servers
        $mail->SMTPAuth = true;                               // Enable SMTP authentication
        $mail->Username = 'user';              // SMTP username
        $mail->Password = 'pass';           // SMTP password
        $mail->SMTPSecure = 'tls';                            // Enable encryption, 'ssl' also accepted


        $mail->From = 'teste';
        $mail->FromName = 'teste';


        $mail->addAddress($usuario->email);  // Add a recipient


        $mail->WordWrap = 50;                                 // Set word wrap to 50 characters
        $mail->isHTML(true);                                  // Set email format to HTML


        $mail->Subject = 'Newsletter';
        $mail->Body = utf8_decode('tesste'); 


        if(!$mail->send()) {
            echo "Ocorreu um erro ao enviar!";
            //echo 'Mailer Error: ' . $mail->ErrorInfo;
        } else {
            echo 'Newsletter enviado com sucesso!';
        }
    }

    return true;
}

}

    
asked by anonymous 18.06.2014 / 21:08

2 answers

3

When it comes to sending newsletters, 8 thousand recipients are not a high number, let alone a number that will consume all available memory.

What seems to be missing is an optimization of the objects in use so as not to fill the memory with the same information repeated 8 thousand times.

Possible optimization

function enviarNewsletterUsuarios() {

    // recolher os destinatários
    $usuarios = getUsuarios();

    // se temos destinatários
    if ($usuarios) {

        /* Preparar o envio definindo os valores comuns
         */
        $mail = new PHPMailer;

        $mail->isSMTP();                // Set mailer to use SMTP
        $mail->Host = 'host';           // Specify main and backup SMTP servers
        $mail->SMTPAuth = true;         // Enable SMTP authentication
        $mail->Username = 'user';       // SMTP username
        $mail->Password = 'pass';       // SMTP password
        $mail->SMTPSecure = 'tls';      // Enable encryption, 'ssl' also accepted

        $mail->From = 'teste';
        $mail->FromName = 'teste';

        $mail->WordWrap = 50;           // Set word wrap to 50 characters
        $mail->isHTML(true);            // Set email format to HTML

        $mail->Subject = 'Newsletter';
        $mail->Body = utf8_decode('tesste');


        /* Por cada destinatário enviar o email
         */
        foreach ($usuarios as $usuario) {

            $mail->addAddress($usuario->email);  // Add a recipient

            if ($mail->send()) {
                // acção quando correu bem
            } else {
                // correu mal :(
            }

            /* Limpa a lista de destinatários para evitar que o email
             * a enviar vá acumulando destinatários
             */
            $mail->ClearAllRecipients();
        }

        return true;
    }
}

In your code I noticed also that you have:

$objetivo = getObjetivo($usuario->guid);
$ideias   = getIdeiaPorObjetivo($objetivo, $usuario->cidade);

In the case of something that is going to be "injected" into the body of the email or the subject of the email, I already recommend a queue sending in database because the job of sending the email is this even send the email. The information on it should be ready and ready to use or actually face high memory consumption issues.

Example of a table for queue of newsletters:

CREATE TABLE IF NOT EXISTS 'newsletter_queue' (
  'id' int(13) NOT NULL AUTO_INCREMENT COMMENT 'Internal ID',
  'newsletter_id' int(13) NOT NULL COMMENT 'ID from the table "newsletter"',
  'entity_id' int(13) NOT NULL COMMENT 'ID from the table "entity".',
  'entity_type' enum('entity','subscriber','admin user','unknown') NOT NULL DEFAULT 'entity' COMMENT 'The entity type for statistics.',
  'send_method' set('mail','smtp') NOT NULL DEFAULT '' COMMENT 'Message is sent using PHP mail() or SMTP.',
  'from_email' varchar(255) NOT NULL COMMENT 'Sets the From email address for the message',
  'from_name' varchar(255) NOT NULL COMMENT 'Sets the From name of the message',
  'smtp_host' varchar(255) NOT NULL COMMENT 'Sets the SMTP hosts. All hosts must be separated by a semicolon. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). Hosts will be tried in order.',
  'smtp_port' int(4) NOT NULL DEFAULT '25' COMMENT 'Sets the default SMTP server port.',
  'smtp_auth' tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Sets SMTP authentication. Utilizes the Username and Password variables.',
  'smtp_secure' enum('','ssl','tls','starttls') NOT NULL DEFAULT '' COMMENT 'Sets connection prefix.',
  'smtp_username' varchar(255) NOT NULL COMMENT 'Sets SMTP username.',
  'smtp_password' varchar(255) NOT NULL COMMENT 'Sets SMTP password.',
  'recipient_mail' varchar(255) NOT NULL,
  'recipient_name' varchar(255) NOT NULL,
  'attachment' varchar(255) NOT NULL,
  'content_type' varchar(50) NOT NULL,
  'priority' enum('1','3','5') NOT NULL DEFAULT '3' COMMENT 'Email priority (1 = High, 3 = Normal, 5 = low)',
  'charset' enum('iso-8859-1','utf-8','windows-1252') NOT NULL DEFAULT 'utf-8' COMMENT 'Sets the CharSet of the message.',
  'send_date' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY ('id'),
  KEY 'newsletter_id' ('newsletter_id'),
  KEY 'entity_id' ('entity_id'),
  KEY 'entity_type' ('entity_type')
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

With a table like this, you have a function that adds emails to queue and your send function will only send X emails every X minutes. This way you will definitely solve memory problems now or in the future when your CRON runs to send emails.

    
18.06.2014 / 22:44
0

One thing you should keep in mind is that in terms of performance, arrays are evil. You should never have very large arrays.

In the scenario of your problem, the thing is worse because it is the sending of e-mail. You have some possible outputs but in general the idea is basically to work with the concept of results pagination.

List a "little", iterate and send. Unlike a pagination, which usually retrieve 10, 20, or 50 records, you can even go further.

Regarding GUI, you can make a splash screen with a META Refresh of X seconds that it serves, including for the operator, if any, to know that the system continues to function.

Here, an important factor would be to have a way to control whether a recipient has already been forwarded to that recipient. A column in the database, in a table of logs, is suddenly enough.

This is because the action depends on the hardware being operative for the send to occur. And if there is any power outage you may have clients / users receiving duplicates.

If it's batch, which is interesting for 8,000 records, scheduling with CronJobs would be well desirable and nothing would not be needed.

However, everything depends on the limitations imposed by your server. Just remember that SPAM is a crime! ;)

    
18.06.2014 / 21:54