Page 1 of 1

Possible bug?

Posted: Mon Jan 07, 2008 9:16 pm
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.

Posted: Tue Jan 08, 2008 2:31 am
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:

Posted: Tue Jan 08, 2008 12:44 pm
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!