Possible bug?

Swift Mailer is a fantastic library for sending email with php. Discuss this library or ask any questions about it here.

Moderators: Chris Corbyn, General Moderators

Post Reply
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Possible bug?

Post by s.dot »

Consider the following code, in which I am using swift along with smtp authentication, decorator plugin and anti-flood plugin.

Code: Select all

set_time_limit(0);
ignore_user_abort(true);
 
if (empty($_POST['emailSubject']) || empty($_POST['emailBody']))
{
    die('Please provide an email body AND a subject');
}
 
$subject = stripslashes($_POST['emailSubject']);
$body = stripslashes($_POST['emailBody']);
 
//load in the components
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/Swift.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/Swift/Connection/SMTP.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/Swift/Plugin/Decorator.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/Swift/Plugin/AntiFlood.php';
 
//load the SMTP connection and authenticate
$smtp = new Swift_Connection_SMTP('smtp.1and1.com', 25);
$smtp->setUsername('me@mydomain.tld');
$smtp->setpassword('password');
 
//start up swift with our SMTP connection
$swift = new Swift($smtp);
 
//email message with subject only
$message = new Swift_Message($subject);
 
//add our components
$message->attach(new Swift_Message_Part(strip_tags($body)));
$message->attach(new Swift_Message_Part($body, 'text/html')); 
 
//recipient list
$recipients = new Swift_RecipientList();
 
//replacements array
$replacements = array();
 
//grab our email list, loop through adding to recipients and populating the replacements array
$r = mysql_query("SELECT `email`, `username` FROM `users` WHERE `emailGeneral` = 1") or die(mysql_error());
while ($a = mysql_fetch_assoc($r))
{
    $recipients->addTo($a['email']);
    $replacements[$a['email']] = array('{user}' => $a['username']);
}
 
//Load the plugin with these replacements
$swift->attachPlugin(new Swift_Plugin_Decorator($replacements), 'decorator');
 
//Reconnect after 90 emails, but wait for 10 seconds first
$swift->attachPlugin(new Swift_Plugin_AntiFlood(90, 10), 'anti-flood');
 
//send
$numSent = $swift->batchSend($message, $recipients, 'me@mydomain.tld');
 
echo $numSent . ' emails sent out of ' . mysql_num_rows($r) . ' possible!';
The output of this script is:

Code: Select all

274 emails sent out of 276 possible!
That's all fine and dandy.

However a few users have complained to me that their username is wrong. I'm wondering what happens to the $replacements array when an email can't be sent for whatever reason? Is this a problem in the swift decorator plugin or my code? Everything seemed fine when all of the emails were sent when I had less members. I'm just grabbing at straws here, but I'm thinking when an email can't be sent it throws off the replacements array.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Sounds like a bug yeah, esp. since someone else mentioned something like this before. Unfortunately there aren't going to be any more 3.x releases since I'm well and truly tearing into the PHP5-only 4.x version. Everything that's in 3.x will be available when I make a new release though; I'm aiming for the GoPHP5 epoch of 8th February though I'm running a little behind right now.

I've just had a check and see the bug. Returning the message to it's original state uses the SendListener interface:

Code: Select all

 
  public function sendPerformed(Swift_Events_SendEvent $e)
  {
    $message = $e->getMessage();
    $this->recursiveRestore($message, $this->store);
    $this->store = null;
  }
However, if the message wasn't sent this method never runs and thus, the replacement tags will already be replaced with the previous replacements.

Try this fix (in Swift/Plugin/Decorator.php):

In the class declaration, remove the SendListener from the list of implemented interfaces:

Code: Select all

//change
class Swift_Plugin_Decorator implements Swift_Events_BeforeSendListener, Swift_Events_SendListener
{
 
//to
class Swift_Plugin_Decorator implements Swift_Events_BeforeSendListener
{
Around line 208, in the recursiveRestore() method add the following lines:

Code: Select all

 protected function recursiveRestore(Swift_Message_Mime $mime, &$store)
  {
    if (empty($store)) //start added code
    {
      return;
    } //end added code
    
    //Restore headers
    foreach ($store["headers"] as $name => $array)
    {
At round line 110 add the following lines:

Code: Select all

 public function beforeSendPerformed(Swift_Events_SendEvent $e)
  {
    $message = $e->getMessage();
    $this->recursiveRestore($message, $this->store); //added
 
It's not tested but it looks like that would fix it.

EDIT | Makes me think I need SendSuccessListener and SendFailureListener in the 4.x version :idea:
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

Thanks Chris! I'm sure that solution will be helpful for other people. Right now, since there's not OODLES of members, I'm just going to use $swift->send() inside of my loop from the database, customizing the message inside of there. I can't risk it with it being live and a sort of business web site. Looking forward to version 4!
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply