草庐IT

PHP 和 SwiftMailer : Decorator Plugin 'stuck' on first entry.

coder 2024-04-16 原文

我在使用 SwiftMailer 的最新主要版本时遇到问题,装饰器插件只会替换消息中列表中第一个电子邮件地址的占位符,然后在所有后续电子邮件中使用相同的数据 - 无论电子邮件地址。

例如如果...

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

第一封电子邮件可能会说...“嗨,杰夫,你今年 32 岁”。 然后第二封电子邮件应该说“嗨,玛丽,你 86 岁了”。但是,第二封电子邮件与第一封电子邮件完全相同。 有任何想法吗?我感谢任何和所有帮助...谢谢。

这是我的php代码

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);

这是插件代码

<?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;
    }
  }

}

另一个相关的插件页面

<?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);

}

Reference: 1. swift mailer plugin page: http://swiftmailer.org/docs/plugins.html (Using the Decorator Plugin)

  1. Disscuss on official forum 1. https://github.com/swiftmailer/swiftmailer/issues/101

  2. Disscuss on official forum 2. https://github.com/swiftmailer/swiftmailer/issues/161

最佳答案

send() 方法不应该作为循环的一部分被调用吗?否则,您只是遍历所有用户,并且只会在用户数组的最后一个元素上调用 send()。

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);
    }
}

调用代码负责调用 beforeSendPerformed() 方法似乎很奇怪,但如果是这样的话,我会在 foreach 中的 send() 之前添加它循环。

-- 更新--

试试这个:

$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);
}

关于PHP 和 SwiftMailer : Decorator Plugin 'stuck' on first entry.,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9434883/

有关PHP 和 SwiftMailer : Decorator Plugin 'stuck' on first entry.的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  7. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

  8. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  9. ruby-on-rails - Rails 中的 NoMethodError::MailersController#preview undefined method `activation_token=' for nil:NilClass - 2

    似乎无法为此找到有效的答案。我正在阅读Rails教程的第10章第10.1.2节,但似乎无法使邮件程序预览正常工作。我发现处理错误的所有答案都与教程的不同部分相关,我假设我犯的错误正盯着我的脸。我已经完成并将教程中的代码复制/粘贴到相关文件中,但到目前为止,我还看不出我输入的内容与教程中的内容有什么区别。到目前为止,建议是在函数定义中添加或删除参数user,但这并没有解决问题。触发错误的url是http://localhost:3000/rails/mailers/user_mailer/account_activation.http://localhost:3000/rails/mai

  10. ruby-on-rails - Rails - 使用/自定义 URL : '/dashboard' 指定根路径 - 2

    如何使此根路径转到:“/dashboard”而不仅仅是http://example.com?root:to=>'dashboard#index',:constraints=>lambda{|req|!req.session[:user_id].blank?} 最佳答案 您可以通过以下方式实现:root:to=>redirect('/dashboard')match'/dashboard',:to=>"dashboard#index",:constraints=>lambda{|req|!req.session[:user_id].b

随机推荐