PHP and SwiftMailer: Decorator plugin gets stuck on first login.

I had a problem with the latest major version of SwiftMailer, where the decorator plugin will replace the placeholder in the message for the first email address in the list and then will use the same data for all of the following emails - regardless of the email address.

For example, if ...

$replacements[ test@test.com ] = array('{firstname}'=>'Jeff', '{age}'=>'32'); $replacements[ example@example.com ] = array('{firstname}'=>'Mary', '{age}'=>'86'); 

The first letter may say ... "Hi Jeff, you are 32." And then the second letter should say: "Hello, Mary, to you 86." But instead, the second letter is identical to the first. Any ideas? I appreciate any help with this ... Thanks.

This is my php code

 foreach ($result as $user) { $replacements[$user['Email']] = array( '{FirstName}'=>$user['FirstName'], '{LastName}'=>$user['LastName'] ); } $mailer = Swift_Mailer::newInstance($transport); $decorator = new Swift_Plugins_DecoratorPlugin($replacements); $mailer->registerPlugin($decorator); $message = Swift_Message::newInstance() ->setSubject('Important notice for {FirstName}') ->setFrom(array(' john@doe.com ' => 'John Doe')) ->setBody( "Hello {FirstName}, we have reset your password to {LastName}\n" . "Please log in and change it at your earliest convenience." ) ; foreach ($result as $user) { $message->setTo($user['Email']); } // Create a message //$template = file_get_contents('../html/full_width.html'); //->setBody($template, 'text/html', 'utf-8'); //->addPart('Dear {FirstName} {LastName},This is testing mail.', 'text/plain', 'utf-8'); // Send the message // Pass a variable name to the send() method if (!$numSent=$mailer->send($message,$failures)) { echo "Failures:"; print_r($failures); } else printf("Sent %d messages\n", $numSent); 

And this is the plugin code

 <?php /* * This file is part of SwiftMailer. * (c) 2004-2009 Chris Corbyn * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Allows customization of Messages on-the-fly. * * @package Swift * @subpackage Plugins * * @author Chris Corbyn * @author Fabien Potencier */ class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements { /** The replacement map */ private $_replacements; /** The body as it was before replacements */ private $_orginalBody; /** The original headers of the message, before replacements */ private $_originalHeaders = array(); /** Bodies of children before they are replaced */ private $_originalChildBodies = array(); /** The Message that was last replaced */ private $_lastMessage; /** * Create a new DecoratorPlugin with $replacements. * * The $replacements can either be an associative array, or an implementation * of {@link Swift_Plugins_Decorator_Replacements}. * * When using an array, it should be of the form: * <code> * $replacements = array( * " address1@domain.tld " => array("{a}" => "b", "{c}" => "d"), * " address2@domain.tld " => array("{a}" => "x", "{c}" => "y") * ) * </code> * * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, * the object should return just the array of replacements for the address * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. * * @param mixed $replacements */ public function __construct($replacements) { if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { $this->_replacements = (array) $replacements; } else { $this->_replacements = $replacements; } } /** * Invoked immediately before the Message is sent. * * @param Swift_Events_SendEvent $evt */ public function beforeSendPerformed(Swift_Events_SendEvent $evt) { $message = $evt->getMessage(); $this->_restoreMessage($message); $to = array_keys($message->getTo()); $address = array_shift($to); if ($replacements = $this->getReplacementsFor($address)) { $body = $message->getBody(); $search = array_keys($replacements); $replace = array_values($replacements); $bodyReplaced = str_replace( $search, $replace, $body ); if ($body != $bodyReplaced) { $this->_originalBody = $body; $message->setBody($bodyReplaced); } foreach ($message->getHeaders()->getAll() as $header) { $body = $header->getFieldBodyModel(); $count = 0; if (is_array($body)) { $bodyReplaced = array(); foreach ($body as $key => $value) { $count1 = 0; $count2 = 0; $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; $bodyReplaced[$key] = $value; if (!$count && ($count1 || $count2)) { $count = 1; } } } else { $bodyReplaced = str_replace($search, $replace, $body, $count); } if ($count) { $this->_originalHeaders[$header->getFieldName()] = $body; $header->setFieldBodyModel($bodyReplaced); } } $children = (array) $message->getChildren(); foreach ($children as $child) { list($type, ) = sscanf($child->getContentType(), '%[^/]/%s'); if ('text' == $type) { $body = $child->getBody(); $bodyReplaced = str_replace( $search, $replace, $body ); if ($body != $bodyReplaced) { $child->setBody($bodyReplaced); $this->_originalChildBodies[$child->getId()] = $body; } } } $this->_lastMessage = $message; } } /** * Find a map of replacements for the address. * * If this plugin was provided with a delegate instance of * {@link Swift_Plugins_Decorator_Replacements} then the call will be * delegated to it. Otherwise, it will attempt to find the replacements * from the array provided in the constructor. * * If no replacements can be found, an empty value (NULL) is returned. * * @param string $address * * @return array */ public function getReplacementsFor($address) { if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { return $this->_replacements->getReplacementsFor($address); } else { return isset($this->_replacements[$address]) ? $this->_replacements[$address] : null ; } } /** * Invoked immediately after the Message is sent. * * @param Swift_Events_SendEvent $evt */ public function sendPerformed(Swift_Events_SendEvent $evt) { $this->_restoreMessage($evt->getMessage()); } // -- Private methods /** Restore a changed message back to its original state */ private function _restoreMessage(Swift_Mime_Message $message) { if ($this->_lastMessage === $message) { if (isset($this->_originalBody)) { $message->setBody($this->_originalBody); $this->_originalBody = null; } if (!empty($this->_originalHeaders)) { foreach ($message->getHeaders()->getAll() as $header) { $body = $header->getFieldBodyModel(); if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); } } $this->_originalHeaders = array(); } if (!empty($this->_originalChildBodies)) { $children = (array) $message->getChildren(); foreach ($children as $child) { $id = $child->getId(); if (array_key_exists($id, $this->_originalChildBodies)) { $child->setBody($this->_originalChildBodies[$id]); } } $this->_originalChildBodies = array(); } $this->_lastMessage = null; } } } 

another related plugin page

 <?php /* * This file is part of SwiftMailer. * (c) 2004-2009 Chris Corbyn * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Allows customization of Messages on-the-fly. * * @package Swift * @subpackage Plugins * * @author Chris Corbyn */ interface Swift_Plugins_Decorator_Replacements { /** * Return the array of replacements for $address. * * This method is invoked once for every single recipient of a message. * * If no replacements can be found, an empty value (NULL) should be returned * and no replacements will then be made on the message. * * @param string $address * * @return array */ public function getReplacementsFor($address); } 

Link: 1. quick page plugin page: http://swiftmailer.org/docs/plugins.html (using Decorator plugin)

+4
source share
1 answer

Shouldn't the send () method be called as part of a loop? Otherwise, you just iterate through all users, and send () will be called only by the last element of the users array.

 foreach ($result as $user) { $message->setTo($user['Email']); // Pass a variable name to the send() method if (!$numSent=$mailer->send($message,$failures)) { echo "Failures:"; print_r($failures); } } 

It seems strange that the calling code is responsible for calling the beforeSendPerformed() method, but if that happens, I will add it before send() inside the foreach loop.

- Update -

Try the following:

 $mailer = Swift_Mailer::newInstance($transport); $message = Swift_Message::newInstance() ->setSubject('Important notice for {FirstName}') ->setFrom(array(' john@doe.com ' => 'John Doe')); foreach ($result as $user) { $message->setTo($user['Email']); $message->setBody(setBody(sprintf("Hello %s, we have reset your password to %s\n" . "Please log in and change it at your earliest convenience.", $user['FirstName'], $user['LastName']); if (!$numSent=$mailer->send($message,$failures)) { $failures[] = $failures; } } if (isset($failures)) { var_dump($failures); } 
+4
source

Source: https://habr.com/ru/post/1398267/


All Articles