Wrong CC's being sent when sending emails asynchronously

the mail() function

Moderators: egami, macek, gesf

Post Reply
User avatar
juliogm
New php-forum User
New php-forum User
Posts: 3
Joined: Fri Mar 09, 2018 3:25 pm

Fri Mar 09, 2018 3:28 pm

Hello,

I am writing a PHP module to send emails to multiple addresses asynchronously. The emails will also have come CC's of the corresponding group.

I have used the following code as my basis for the module: https://stackoverflow.com/questions/365 ... 5#36592255

The emails are being sent successfully, however there is one big problem: some of the emails are being CC'd to emails that are not from their corresponding groups. This has caused confusion and frustration to us.

Why does this happen?

My hunch is that I am not passing the values by reference in the foreach loop, but I am not sure.

Can you please help me? Thanks!

To give you a better idea of what I am doing to send multiple emails asynchronously, I will add a general snippet of the code I do:

Code: Select all

while($somethingWithEmailHasToBeSent){
	unset($mail_CC); //I have to clear whatever was in that variable
	$mail_CC = array(); //re-declare it
	
	/*
		I add the other stuff needed, $mail_from, $from_name, $mail_CC[], $subject, $message
	*/
	
	 AsynchMail::addMail($subject, $mail_to, $mail_CC, $mail_from, $from_name, $message, $tieneAdjuntos, $adjuntos, $nombresAdjuntos);//add to queue
}

register_shutdown_function("AsynchMail::send"); //When I'm done adding emails to the queue, send them
This is the AsynchMail.php:

Code: Select all

<?php
use PHPMailer\PHPMailer\PHPMailer;
require '../vendor/autoload.php';
require '../PHPMailer/src/PHPMailer.php';
require '../PHPMailer/src/Exception.php';
require '../PHPMailer/src/OAuth.php';
require '../PHPMailer/src/POP3.php';
require '../PHPMailer/src/SMTP.php';

class AsynchMail{
    public static $mails = [];
    
    public static function addMail($subject, $mail_to, $mail_CC = null, $mail_from, $from_name, $message, $tieneAdjuntos = false, $adjuntos = null, $nombresAdjuntos = null){
        self::$mails[] = [
            "subject" => $subject,
            "to" => $mail_to,
            "CC"=>$mail_CC,
            "from"=>$mail_from,
            "from_name"=>$from_name,
            "message"=>$message,
            "tieneAdjuntos"=>$tieneAdjuntos,
            "ajuntados"=>$adjuntos,
            "nombresAdjuntados"=>$nombresAdjuntos
        ];
    }
    
    public static function send(){
        foreach(self::$mails as $correo){
            if($correo['tieneAdjuntos'] == true){
                $files = array(); $nameAdjuntos = array();
                if(is_array($correo['ajuntados'])){
                    $files = $correo['ajuntados'];
                    $nameAdjuntos = $correo['nombresAdjuntados'];
                }else{
                    $files[] = $correo['ajuntados'];
                    $nameAdjuntos[] = $correo['nombresAdjuntados'];
                }
            }
            $mail = new phpmailer (true);
            $mail->CharSet = 'UTF-8';
            $mail->SetLanguage("es", 'includes/phpMailer/language/');
            $mail->IsSMTP();
            $mail->Host = 'xxx';  // Specify main and backup SMTP servers
            $mail->SMTPAuth = true;                               // Enable SMTP authentication
            $mail->Username = 'xxx';                 // SMTP username
            $mail->Password = 'xxx';                           // SMTP password
            $mail->SMTPSecure = 'tls';                            // Enable TLS encryption, `ssl` also accepted
            $mail->Port = 587;                                    // TCP port to connect to
            $mail->setFrom($correo['from']);
            $mail->FromName = $correo['from_name'];
            if($correo['to']!= null && $correo['to'] != ""){
                $mail->AddAddress ($correo['to']);
            }else{
                $mail->AddAddress("xxx");
            }
            $mail->AddCC('xxx');
            $mail->AddCC('xxx');
            //$mail->AddCC($correo['to']);
            if($correo["CC"] != null){
            	foreach($correo["CC"] as $CC){
            		$mail->AddCC($CC);
            	}
            }
            $mail -> Subject = $correo['subject'];
            $mail -> Body = $correo['message'];
            $mail -> IsHTML (true);
            $archivos = 'Mi Lybro';
            $msg = "Mensaje Enviado";
            if (count($files) > 0)
            {
                $count = 0;
                $msg .= "<ul>";
                foreach ($files as $fil)
                {
                    $tmp_name = $fil;
                    //  $tmp_name = "20180212TEST2.jpg";
                    $explodefile=explode(".",$tmp_name);
                    $extension = end($explodefile);
                    $filx = realpath(dirname(__FILE__))."/../archivos/$tmp_name";
                    $msg .= "<li>$tmp_name</li>";
                    $name = uniqid('bc') . '_' . $tmp_name;
                    $numero = '';
                    if(count($files) >1 && $count != 0){
                        $numero = "-$count";
                    }
                    if(!$mail -> AddAttachment ($filx, $nameAdjuntos[$count])){
                        echo "<script>alert('whaaa: $filx');</script>";
                    }
                    $count++;
                }
                $msg .= '</ul>';
            }
            
            if (!$mail -> Send ())
            {
                echo "<script>alert('No se pudo enviar el mensaje: {$mail->ErrorInfo}');</script>";
            }else{
                $to = $correo['to'];
                echo "Enviado exitosamente a $to <br>";
            }
        }
    }
}
?>
As requested by php-Rob, I have added an excerpt where this problem happens a lot. This code was modified to have different variables for each CC rather than an array for all of them but I have the original concept commented out so you can see how it worked:

Code: Select all

<?php
/*require_once(__DIR__.'/../accesodatos/consultas.php');
 require_once(__DIR__.'/../default/AsynchMail.php');*/
$soloCargoExtra = true;
$regArticulo = listadoDepositosCeroCC("ENV");
if(mysqli_num_rows($regArticulo)>0)
{
    $stmt19 = detalleCuentaPedidoEspecial_1();
    $stmt20 = detalleFacturaPedidoEspecial_1();
    while($registroDD=mysqli_fetch_assoc($regArticulo))
    {
        //   $mail_CC = "";
        // $mail_CC = array();
        $mail_CC1 = ''; $mail_CC2=''; $mail_CC3='';
        $mail_CC4=''; $mail_CC5='';
        $dep_id = $registroDD['dep_id'];
        $totalAPagar = 0;
        $totalArticulos = 0;
        $cuentaEspecial = detalleCuentaPedidoEspecial_2($dep_id, $stmt19);
        $especial = $cuentaEspecial['cuenta'];
        $TipoCorreo = detalleFacturaContabilidad2Guia($dep_id);
        if ($TipoCorreo['nombre_archivo'] != '')
        {
            $tipos = 7;
        }else {
            $tipos = 4;
        }
        
        $msjCorreo = datalleCorreosMensajes($tipos);
        $men= $msjCorreo['tit_mensaje'];
        $location = $registroDD['dep_id_proveedor'];
        $ValidacionProveedor = detalleOKProve($location);
        $vincular = $ValidacionProveedor['pro_id_clave'];
        $correoContabilidad = $ValidacionProveedor['pro_contactoC'];
        $correoCC = $ValidacionProveedor['pro_contactoCC'];
        $seVinculaA = $ValidacionProveedor['pro_vincular'];
        if($seVinculaA != ''){
            $validacionVinculo = detalleOKProve($seVinculaA);
            $correoContabilidadVinculo = $validacionVinculo['pro_contactoC'];
            $correoCCVinculo = $validacionVinculo['pro_contactoCC'];
            if($correoCCVinculo != ""){
                // $mail_CC[] = $correoCCVinculo;
                $mail_CC1 = $correoCCVinculo;
            }
        }
        if($correoCC != ""){
            //$mail_CC[] = $correoCC;
            $mail_CC2 = $correoCC;
        }
        //$mail_CC[] = "sssss";
        $Selecvincular = detalleOKProveedorVicular($vincular);
        $mail_to = ($correoContabilidad != "" ? $correoContabilidad : $correoContabilidadVinculo);
        if($correoContabilidad != ""){
            //$mail_CC[] = $correoContabilidadVinculo;
            $mail_CC3 = $correoContabilidadVinculo;
        }
        $mail_CC4='aaaaa';
        $mail_CC5 = 'ssssss';
        
        include_once('class.phpmailer.php');
        $postback = isset($_POST['postback']) ? true : false;
        $mail_from = "ssss";
        $from_name = "ssss";
        $subject = "aaaa";
        $message = "";
        if($especial == 0){
            $message = 'beginning message';
            
            $idDeposito = $registroDD['dep_id'];
            $regDetalleArticulo = DetalleDepositoDetalleMultiple2($idDeposito);
            if(mysqli_num_rows($regDetalleArticulo)>0)
            {
                $soloCargoExtra = false;
                while($registroD=mysqli_fetch_assoc($regDetalleArticulo))
                {
                    $stmt3 = detalleFA_1();
                    if($registroD['depD_id_articulo'] != "Extras")
                    {
                        $articulo = $registroD['depD_id_articulo'];
                        $detalleArtas = detalleFA_2($articulo, $stmt3);
                        $totalISBN = $detalleArtas['art_cantidad'] * $detalleArtas['art_cost'];
                        $message .= 	'>>>>>';
                        $totalAPagar += $totalISBN;
                        $totalArticulos += $detalleArtas['art_cantidad'];
                    }else{
                        $razon = $registroD['depD_comentario'];
                        $message .= 'rerer';
                        $totalAPagar += $registroD['depD_total'];
                    }
                }
                $stmt3->close();
            }
            if($soloCargoExtra){
                $message .= 'rerererer';
                $totalAPagar += $registroD['depD_total'];
            }
            $detalleSaldoF = detalleSaldoAFavordesdeDeposito($dep_id);
            if(floatval($detalleSaldoF['dep_saldofavor']) != 0)
            {
                $stmt21 = detalleComentarioSaldoFavorProveedor_1();
                $saldo = $detalleSaldoF['dep_saldofavor'];
                $proveedor = $location;
                $detalleComentarioSF = detalleComentarioSaldoFavorProveedor_2($proveedor, $saldo, $stmt21);
                $SFComentario = $detalleComentarioSF['dellSaldo_comentario'];
                $totalAPagar -= $detalleSaldoF['dep_saldofavor'];
                $message .= '333333';
                $stmt21->close();
            }
            $message .= 'aaaaaa';
            $message .= 'still message';
        }else{
            $factura = detalleFacturaPedidoEspecial_2($dep_id, $stmt20);
            $message = 'mess';
            $message .= 'age';
        }
        
        if ($postback)
        {
           
            $tieneAdjuntos = true;
            $adjuntos = array();
            $nombresAdjuntos = array();
            if (isset ($_FILES["archivos".$aa]))
            {
                $count = 0;
                foreach ($_FILES["archivos".$aa]["error"] as $key => $error)
                {
                    
                    if ($error == UPLOAD_ERR_OK)
                    {
                        $conteo = '';
                        if($count != 0)
                        {
                            $conteo = "-$count";
                        }
                        $tmp_name = $_FILES["archivos".$aa]["tmp_name"][$key];
                        $namet = $_FILES["archivos".$aa]["name"][$key];
                        $exploded = explode('.', $namet);
                        $extension = end($exploded);
                        $name = $claveP.$conteo.'.'.$extension;
                        $name = date("dmY").$name;
                        $count++;
                    }
                }
            }
            $listaComprobantes = listadoArchivosContabilidad($dep_id, "Comprobante");
            $adjuntos = null;
            $nombresAdjuntos = null;
            if(mysqli_num_rows($listaComprobantes) > 0){
                $adjuntos = array(); $nombresAdjuntos= array();
                while($registroComp = mysqli_fetch_assoc($listaComprobantes)){
                    $adjuntos[] = $registroComp['nombre_archivo'];
                    $nombresAdjuntos[] = $registroComp['nombre_archivo'];
                }
            }
            
            $listaComprobantesGuia = listadoArchivosContabilidad($dep_id, "Guia");
            if(mysqli_num_rows($listaComprobantesGuia) > 0){
                $subject .= " y Número de Guía";
                while($registroCompGuia = mysqli_fetch_assoc($listaComprobantesGuia)){
                    $adjuntos[] = $registroCompGuia['nombre_archivo'];
                    $nombresAdjuntos[] = $registroCompGuia['nombre_archivo'];
                }
            }
            AsynchMail::addMail($subject, $mail_to, $mail_CC1, $mail_CC2, $mail_CC3, $mail_CC4, $mail_CC5, $mail_from, $from_name, $message, $tieneAdjuntos, $adjuntos, $nombresAdjuntos);
            
        }
        
        updateDepositoDetalleEstatus($dep_id);
    }
    $stmt19->close();
    $stmt20->close();
    register_shutdown_function("AsynchMail::send");
    //AsynchMail::send();
}
?>
Last edited by juliogm on Tue Mar 13, 2018 7:49 am, edited 2 times in total.

User avatar
phpRob
New php-forum User
New php-forum User
Posts: 65
Joined: Mon Feb 26, 2018 7:15 am

Fri Mar 09, 2018 8:15 pm

My hunch is that I am not passing the values by reference in the foreach loop, but I am not sure.
Where is this foreach loop you speak of. Please share the relevant code, can't help you without having something to look at. And then also, please share this as you now have me curious about why you want or need to have the mail-sending process asynchronous. Why do you really need that? If your purpose is to just send emails, I don't see the need. If your code is busy doing other work where it must respond to other objects or users I can see the need for it. Please share about the need as I am really wanting to learn a bit about what you might know or be aware of with regard to your "need" for asynchronous operations. Thanks.

User avatar
juliogm
New php-forum User
New php-forum User
Posts: 3
Joined: Fri Mar 09, 2018 3:25 pm

Mon Mar 12, 2018 2:57 pm

Thank you for your reply, phpRob. I have added the class on my post. Also, the project is expected to be used for many users that need things to be done quickly. Therefore, having the emails been sent asynchronously was requested to us.

User avatar
phpRob
New php-forum User
New php-forum User
Posts: 65
Joined: Mon Feb 26, 2018 7:15 am

Mon Mar 12, 2018 7:31 pm

Hello juliogm. I looked at everything and I don't believe you are doing anything wrong with your $mail_CC array in the called functions I see. But you still haven't shown me the code you use to populate your $mail_CC array. This is where the problem is I think. You are likely over-looking something that is occurring in the array's population. You are correctly clearing it at least per your description. I don't think your loop that works with the CC's is the problem either--it all looks fine. I don't think this next item is an issue either but just check to be sure: check your various "$mail->AddCC" calls and make sure unwanted ones do not exist. I want to let you know that the register_shutdown_function does NOT achieve asynchronous work. It does not achieve the goal of multiple independent processes or threads. When people make use of "register_shutdown_function" to "trigger" work to be completed when a script exits, they may feel like they are simulating or even achieving multiple threads...but no. It's all still synchronous. One thread. Sequential. It's just an extra function call to a callback function that is fired prior to the script closing itself out (exiting). In an effort to achieve "async", you've gone with an abstract class that has static members. A bug could also exist because of the static nature of the "$mails" array. I don't have access to all of your code that could be making calls to register a new email to be sent. Check everywhere, check all callers to the function "AsynchMail::addMail()". You might consider eliminating the static array and just sending the email immediately. Sure, use an abstract class for emailing, put don't introduce global variables like you have through the use of the "$mails" array thereby increasing the difficulty of debugging.

User avatar
juliogm
New php-forum User
New php-forum User
Posts: 3
Joined: Fri Mar 09, 2018 3:25 pm

Tue Mar 13, 2018 7:50 am

Hello phpRob, I have added an excerpt of a sample code on my post. Of course some vital info had to be modified for security purposes.

User avatar
phpRob
New php-forum User
New php-forum User
Posts: 65
Joined: Mon Feb 26, 2018 7:15 am

Tue Mar 13, 2018 6:57 pm

Code: Select all

if($correoContabilidad != ""){
            //$mail_CC[] = $correoContabilidadVinculo;
            $mail_CC3 = $correoContabilidadVinculo;
}
I don't know if this is you inconsistently scrambling your code or if you have this bug (above); the variable you test is different from the variable you assign.

Code: Select all

AsynchMail::addMail($subject, $mail_to, $mail_CC1, $mail_CC2, $mail_CC3, $mail_CC4, $mail_CC5, $mail_from, $from_name, $message, $tieneAdjuntos, $adjuntos, $nombresAdjuntos);            
And this function call is just sad because you are not using an array for multiple CC's (1-5)

I'd suggest you start anew and just use a simple function to send an email and call it whenever and from wherever you need it. This should be done and working before you try to optimize for speed. Good luck.

Post Reply