Search code examples
phpmysqlemailslimmailgun

PHP mailing script runs multiple time (using Mailgun)


We have a PHP script tht should send an email to all users that match a certain criteria. The script uses Mailgun to handle the mailing. The script works fine with a small amount of records (about 500 or less), however, if the records become too many (say, 1000, 2000) the script doesn't end and instead runs multiple times (users receive the same email two or three times).

require __DIR__ . '/../vendor/autoload.php';
use Mailgun\Mailgun;

# Instantiate the client.
$mgClient = new Mailgun('our-private-key');
$mgClient1= new Mailgun('our-public-key');
$domain = "our.domain";

// 1. Connect to database

$pdo = new PDO('mysql:host=localhost;dbname=db', 'user', 'psw');
$pdo->exec('SET NAMES "utf8"');


// 2. Query users table for any records where field "endDate" < the current time

$query = "SELECT userId, firstName, email FROM users WHERE endDate <= now() AND sub = 0";
$expiredQuery = $pdo->prepare($query);
$expiredQuery->execute();

$expired = $expiredQuery->fetchAll(PDO::FETCH_OBJ);

if (empty($expired)) {
    echo 'There are no records to unsubscribe.';
    exit();
}


// 3. If found, loop through results and get the "uId"; save users to a users array

$ids = [];
$usernames = [];
$emails = [];

foreach ($expired as $user) {
  $id = $user->uId;
  $username = $user->fName;
  $useremail = $user->email;
  array_push($ids, $id);
  array_push($usernames, $username);
  array_push($emails, $useremail);
}


// 4. Send email to users

$counter = 0;

foreach ($emails as $email) {

  # Issue the call to the client.
  $resultVer = $mgClient1->get("address/validate", array('address' => $email));
  # is_valid is 0 or 1
  $isValid = $resultVer->http_response_body->is_valid;


  if (!$isValid) {
    echo "Not delivered: " . $email . "<br>"; 
    $counter++;
    continue;
  }

  $firstname = $usernames[$counter];

  # Build recipient email
  $rec_msg = 'Hi '.$firstname.'... etc etc';

  # Send mail to recipient
  $result = $mgClient->sendMessage($domain, array(
    'from'    => 'Sender<[email protected]>',
    'to'      => $email,
    'subject' => 'An email',
    'html'    => $rec_msg,

  ), array(
    'inline'  => array('http://www.oursite.com/images/mail_header.jpg', 'http://www.oursite.com/images/footer.jpg')
  ));

  # Increment the counter
  $counter++;
}


// 6. Send notification

echo "The following users were sent an email <br>" . implode(", ", $emails);
die();

I tried adding die() at the end but that didn't help. Also I don't get any output between the multiple script runs.


Solution

  • Two prevent the mailing script from running multiple times at once, I would recommend using a locking component like symfony/lock.

    The other problem is the amount of emails you are trying to send at once. Try to split the number of mails into smaller junks e.g. 500 at once. You can use the SQL LIMIT x,y filter or you fetch all rows and split the rows with array_chunk.