mercredi 2 septembre 2020

PHP strange behaviour on IF statement when sending email via phpmailer (having output of IF and ELSE together)

The script im going to present is quite dificult to understand, but I will explain the basic idea bellow.

<?php

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

require_once 'apps/PHPMailer/src/Exception.php';
require_once 'apps/PHPMailer/src/PHPMailer.php';
require_once 'apps/PHPMailer/src/SMTP.php';

include_once('apps/HTMLDOMParser/simple_html_dom.php');

require_once("../db.php");

function clear($var) {
    $var = NULL;
    unset($var);
}

// Time when emails were sent last time
$result_laststamp = $conn->prepare("SELECT sent FROM nl_campaigns_emails ORDER BY sent DESC LIMIT 1", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$result_laststamp->execute();
if($result_laststamp->rowCount() > 0) {
    while($row_laststamp = $result_laststamp->fetch(PDO::FETCH_ASSOC)) {

        // data for first qeued campaign
        $result_campaign = $conn->prepare("SELECT id,name,text,amount,period,smtp,user FROM nl_campaigns WHERE status='1' ORDER BY id ASC LIMIT 1", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
        $result_campaign->execute();
        if($result_campaign->rowCount() > 0) {

            while($row_campaign = $result_campaign->fetch(PDO::FETCH_ASSOC)) {

                // SMTP Data
                if($row_campaign['smtp']==1 && $row_campaign['user']>0) {

                    $result_smtp = $conn->prepare("SELECT * FROM admin_logins WHERE id=:id LIMIT 1", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
                    $result_smtp->execute(array(":id"=>$row_campaign['user']));
                    if($result_smtp->rowCount() > 0) {

                        while($row_smtp = $result_smtp->fetch(PDO::FETCH_ASSOC)) {

                            // Check if its time to send next group of  emails
                            if($row_laststamp['sent'] < (time()-($row_campaign['period']*3600))) {

                                // get email addresses, send, and edit timestmap in db when they were sent
                                $result_emails = $conn->prepare("SELECT id,email FROM nl_campaigns_emails WHERE (id_campaign=:id_campaign AND sent='0') ORDER BY id ASC LIMIT ".$row_campaign['amount'], array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
                                $result_emails->execute(array(":id_campaign"=>$row_campaign['id']));
                                if($result_emails->rowCount() > 0) {
                                    $array_email_ids = array();
                                    while($row_emails = $result_emails->fetch(PDO::FETCH_ASSOC)) {

                                        try {
                                            $mail = new PHPMailer(true);

                                            $mail->CharSet = 'UTF-8';                                   // UTF8 Encoding
                                            $mail->Encoding = 'base64';                                 // Needs to be set with UTF8
                                            //$mail->SMTPDebug = SMTP::DEBUG_SERVER;                        // Enable verbose debug output
                                            $mail->isSMTP();                                            // Send using SMTP
                                            $mail->Host       = "smtp.somedomain.com";                 // Set the SMTP server to send through
                                            $mail->SMTPAuth   = true;                                   // Enable SMTP authentication
                                            $mail->Username   = "noreply@somedomain.cz";                     // SMTP username
                                            $mail->Password   = "somepassword";                // SMTP password
                                            $mail->SMTPSecure = "tls";
                                            $mail->Port       = 587;                 // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above

                                            //Sender
                                            $mail->setFrom($row_smtp['email'], 'somename');
                                            $mail->addReplyTo('info@somedomain.cz', 'somename');
                                            $mail->Sender = 'noreply@somedomain.cz';

                                            // Recipient
                                            $mail->addAddress($row_emails['email']);


                                            // Attachments
                                            //$mail->addAttachment('/var/tmp/file.tar.gz');         // Add attachments
                                            //$mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name

                                            $bottom_text  = "<p align=center style=\"font-size:11px; font-family:Arial; color:#8b8b8b;\">This email was sent to address <a href=\"mailto:".$row_emails['email']."\">".$row_emails['email']."</a>.<br>";
                                            $bottom_text .= "To unsubscribe you can click to <a href=\"http://www.somedomain.cz/unsubscribe.php?email=".$row_emails['id']."\">unsubscribe</a>.</p>";
                                            $bottom_text .= "<img src=\"http://www.somedomain.cz/view.php?id_campaign=".$row_campaign['id']."&id_email=".$row_emails['id']."\" style=\"visibility: hidden;\">";

                                            // YOUTUBE LINK
                                            $text = new simple_html_dom();
                                            $text->load($row_campaign['text']);
                                            foreach($text->find('iframe') as $element) {
                                                $youtubeid = substr($element->src, strrpos($element->src, "/")+1);
                                                // Create image instances
                                                $video = imagecreatefromjpeg('https://img.youtube.com/vi/'.$youtubeid.'/maxresdefault.jpg');
                                                $button = imagecreatefrompng('../images/youtube_play.png');
                                                $video = imagescale($video, $element->width, $element->height);
                                                $button = imagescale($button, imagesx($video)/7.5);
                                                    
                                                imagecopy($video, $button, imagesx($video)/2-imagesx($button)/2, imagesy($video)/2-imagesy($button)/2, 0, 0, imagesx($button), imagesy($button));

                                                // Output and free from memory
                                                imagejpeg($video, "../images/campaigns/videos/".$youtubeid.".jpg", 95);
                                                imagedestroy($video);
                                                imagedestroy($button);
                                                unset($video);
                                                unset($button);
                                                    
                                                $element->outertext = '<a href="https://www.youtube.com/watch?v='.$youtubeid.'" target="_blank"><img src="http://www.somedomain.cz/images/campaigns/videos/'.$youtubeid.'.jpg" width="'.$element->width.'" height="'.$element->height.'"></a>';
                                            }
                                            $row_campaign['text'] = (string)$text;
                                            $text->clear(); 
                                            clear($text);

                                            // URL LINK FOR CLICKER ANALYZER
                                            $doc = new simple_html_dom();
                                            $doc->load($row_campaign['text']);
                                            foreach ($doc->find('a') as $a) {
                                                $a->href = 'http://www.somedomain.cz/clicker.php?id_campaign='.$row_campaign["id"].'&id_email='.$row_emails["id"].'&url='.urlencode($a->href);
                                            }
                                            $row_campaign['text'] = (string)$doc;
                                            $doc->clear(); 
                                            clear($doc);

                                            // Content
                                            $mail->isHTML(true);                                  // Set email format to HTML
                                            $mail->Subject = $row_campaign['name'];
                                            $mail->Body    = $row_campaign['text'].$bottom_text;
                                            $mail->AltBody = strip_tags($row_campaign['text']);

                                            // MessageID
                                            $data = array(
                                                'campaign_id' => str_pad($row_campaign['id'], 6, '0', STR_PAD_LEFT),
                                                'email_id' => str_pad($row_emails['id'], 11, '0', STR_PAD_LEFT),
                                                'sent' => time(),
                                            );
                                            $messageId = '<'.base64_encode(json_encode($data)).'@www.somedomain.cz>';
                                            $mail->MessageID = $messageId;
                                            $mail->addCustomHeader('In-Reply-To', $messageId);
                                            clear($data);

                                            // SEND Email
                                            $mail->send();
                                        }
                                        catch (Exception $e) {
                                            echo "Emails were not send because: {$mail->ErrorInfo}";
                                            die();
                                        }

                                        // ADD sent email to array
                                        array_push($array_email_ids, $row_emails['id']);

                                        // UPDATE the "sent" stamp
                                        $update = $conn->prepare("UPDATE nl_campaigns_emails SET sent=:sent WHERE id=:id");
                                        try { $update->execute(array(":id"=>$row_emails['id'], ":sent"=>time())); }
                                        catch(Exception $error) { die($error->getMessage()); }
                                        clear($update);

                                        clear($mail);
                                    }
                                    clear($result_emails);

                                    // CHECK IF STATUS COULD BE CLOSED
                                    $result_emails_sent = $conn->prepare("SELECT id FROM nl_campaigns_emails WHERE (id_campaign=:id_campaign AND sent='0') ORDER BY id", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
                                    $result_emails_sent->execute(array(":id_campaign"=>$row_campaign['id']));
                                    if($result_emails_sent->rowCount() == 0) {
                                        $update = $conn->prepare("UPDATE nl_campaigns SET status=:status WHERE id=:id");
                                        try { $update->execute(array(":status"=>3, ":id"=>$row_campaign['id'])); }
                                        catch(Exception $error) { die($error->getMessage()); }
                                        clear($update);
                                    }
                                    clear($result_emails_sent);

                                    // Print the result
                                    echo "<div style=\"color:#1b8d3d; text-align:center; font-size:25px; font-family:Arial;\">Sent total: ".count($array_email_ids)." emails.</div>";
                                    echo "<center><a href=emailing.php style=\"color:black; text-align:center; font-size:25px; font-family:Arial; text-decoration:none;\">REFRESH</a></center>";
                                }
                            }
                            else {
                                echo "<div style=\"color:#1b8d3d; text-align:center; font-size:25px; font-family:Arial;\">Next emails can be send at: ".date("j.n.Y H:i:s", $row_laststamp['sent']+($row_campaign['period']*3600))."</div>";
                                echo "<center><a href=emailing.php style=\"color:black; text-align:center; font-size:25px; font-family:Arial; text-decoration:none;\">REFRESH</a></center>";
                                die();
                            }
                        }

                    }
                    else { die("error resolving SMTP"); }
                    clear($result_smtp);
                }

            }
            clear($result_campaign);
        }
        else {
            echo "<div style=\"color:#1b8d3d; text-align:center; font-size:25px; font-family:Arial;\">No campaign is running</div>";
            echo "<center><a href=emailing.php style=\"color:black; text-align:center; font-size:25px; font-family:Arial; text-decoration:none;\">REFRESH</a></center>";
            die();
        }
    }
}
clear($result_laststamp);
clear($conn);

?>

the script looks quite huge, but at the end its simple logic:

  1. get the last time emails were sent (for example $lastTimeSent)
  2. get the data of the opened queued campaign (for example $dataCampaign)
  3. get SMTP data to be used for sending emails (for example $SMTP)
  4. checks if current timestamp time() is bigger then $lastTimeSent+allowed_period (because Im allowed to send 200 emails every 20mins)
  5. loop the phpmailer, send emails separately (each time add one email to send)

and the problem is, that if I run the script inside nonallowed time period (so im not following rule of 200 emails in 20 mins), all works fine and script will evaluate this as false:

if($row_laststamp['sent'] < (time()-($row_campaign['period']*3600)))
$row_laststamp['sent'] is the timestmap of last email sent ($lastTimeSent)
$row_campaign['period'] is allowed period to send the email (value is actually 0.33 as in hours its 20 minutes), and im multiplying it by 3600 to recalculate to seconds so I can compare
  • so basicaly will jump and do stuff only from ELSE statement as its logical and its OK behaviour

However, when im outsode of the period so the same statement would evaluate as TRUE, the very strange behaviour happens. The script will be looping and sending emails as it should, however instead of sending 200 emails as its the value of $row_campaign['amount'], it actually sends 64 (sometimes 65 emails) and then it wont echo Sent total: ".count($array_email_ids)." emails as it should logicaly, but also shows again the ELSE output *Next emails can be send at: ".date("j.n.Y H:i:s", $row_laststamp['sent']+($row_campaign['period']3600))..

So Im dealing with the problem that its shows the IF and ELSE output at once... In other words, when statement *if($row_laststamp['sent'] < (time()-($row_campaign['period']3600))) is TRUE, it will partialy run the loop of try {} (only 65 times out of 200) and then shows the ELSE {} anyway...

BUT !!! if I remove/comment the line $mail->send(); then ALL works as expected !!!!! (unless emails are sent of course).

I was checking the memory status if im not getting out of memory and found out the maximum in final loop is about 24MB (when limnit is set to 256MB), also execution time is around 10second when I have set 120s.

so basicaly PHPMailer wont send more then 200 emails in loop when sending just one each time?

I wouldnt mind to send them all together and not looping it, however i dont want them to be seen in "TO" header of email, and if I use BCC instead, then (at least in gmail) it jumps to spam.

Any ideas?

Aucun commentaire:

Enregistrer un commentaire